logicloom-nextjs-starter
Version:
A production-ready Next.js starter template with authentication, i18n, dark mode, and modern patterns
229 lines (208 loc) • 6.42 kB
text/typescript
import { AxiosError } from "axios";
import { z } from "zod";
export interface ApiError {
message: string;
code?: string;
field?: string;
details?: any;
}
export class AppError extends Error {
code: string;
statusCode: number;
details?: any;
constructor(
message: string,
code: string = "UNKNOWN_ERROR",
statusCode: number = 500,
details?: any
) {
super(message);
this.name = "AppError";
this.code = code;
this.statusCode = statusCode;
this.details = details;
}
}
export const errorHandler = {
// Handle Axios errors
handleAxiosError: (error: unknown): AppError => {
if (error instanceof AxiosError) {
const status = error.response?.status;
const data = error.response?.data;
// Network error
if (!error.response) {
return new AppError(
"Network error - Cannot connect to server",
"NETWORK_ERROR",
0,
{ originalError: error.message }
);
}
// Server returned error
switch (status) {
case 400:
return new AppError(
data?.message || "Invalid request",
"BAD_REQUEST",
400,
data
);
case 401:
return new AppError(
data?.message || "Authentication failed",
"UNAUTHORIZED",
401,
data
);
case 403:
return new AppError(
data?.message || "Access denied",
"FORBIDDEN",
403,
data
);
case 404:
return new AppError(
data?.message || "Resource not found",
"NOT_FOUND",
404,
data
);
case 409:
return new AppError(
data?.message || "Conflict - Resource already exists",
"CONFLICT",
409,
data
);
case 422:
return new AppError(
data?.message || "Validation failed",
"VALIDATION_ERROR",
422,
data
);
case 429:
return new AppError(
"Too many requests - Please try again later",
"RATE_LIMIT",
429,
data
);
case 500:
return new AppError(
data?.message || "Server error - Please try again",
"SERVER_ERROR",
500,
data
);
default:
return new AppError(
data?.message || "An error occurred",
"UNKNOWN_ERROR",
status || 500,
data
);
}
}
// Generic error
return new AppError(
error instanceof Error ? error.message : "An unknown error occurred",
"UNKNOWN_ERROR",
500
);
},
// Handle Zod validation errors
handleZodError: (error: z.ZodError): AppError => {
const errors = error.issues.map((issue) => ({
field: issue.path.join("."),
message: issue.message,
code: issue.code,
}));
return new AppError("Validation failed", "VALIDATION_ERROR", 400, { errors });
},
// Get user-friendly error message
getUserMessage: (error: unknown): string => {
if (error instanceof AppError) {
return error.message;
}
if (error instanceof AxiosError) {
const appError = errorHandler.handleAxiosError(error);
return appError.message;
}
if (error instanceof z.ZodError) {
const appError = errorHandler.handleZodError(error);
return appError.details.errors[0]?.message || "Validation failed";
}
if (error instanceof Error) {
return error.message;
}
return "An unexpected error occurred";
},
// Log error details (for debugging)
logError: (error: unknown, context?: string) => {
try {
const errorTitle = `❌ Error ${context ? `in ${context}` : ""}`;
// Use console.group if available, otherwise just log title
if (typeof console.group === "function") {
console.group(errorTitle);
} else {
console.error(errorTitle);
}
if (error instanceof AppError) {
console.error("Code:", error.code);
console.error("Status:", error.statusCode);
console.error("Message:", error.message);
if (error.details) {
try {
console.error("Details:", JSON.stringify(error.details, null, 2));
} catch {
console.error("Details:", "[Could not stringify]");
}
}
} else if (error instanceof AxiosError) {
console.error("Type: Axios Error");
console.error("Status:", error.response?.status || "No status");
console.error("Message:", error.message || "No message");
if (error.response?.data) {
try {
console.error("Response:", JSON.stringify(error.response.data));
} catch {
console.error("Response:", "[Could not stringify response]");
}
}
if (error.config) {
console.error("Config:", {
url: error.config.url || "unknown",
method: error.config.method || "unknown",
});
}
} else if (error instanceof z.ZodError) {
console.error("Type: Validation Error");
try {
console.error("Issues:", JSON.stringify(error.issues, null, 2));
} catch {
console.error("Issues:", "[Could not stringify issues]");
}
} else if (error instanceof Error) {
console.error("Type: Error");
console.error("Message:", error.message);
console.error("Stack:", error.stack || "No stack trace");
} else {
console.error("Type: Unknown Error");
try {
console.error("Error:", JSON.stringify(error));
} catch {
console.error("Error:", String(error));
}
}
if (typeof console.groupEnd === "function") {
console.groupEnd();
}
} catch (logError) {
// Fallback if logging itself fails
console.error("Failed to log error:", logError);
console.error("Original error:", error);
}
},
};