{
  "slug": "error-handler",
  "runtimes": {
    "node": {
      "frameworks": {
        "express": {
          "dependencies": {
            "runtime": [],
            "dev": []
          },
          "env": [],
          "architectures": {
            "mvc": {
              "files": [
                {
                  "type": "file",
                  "path": "src/utils/api-error.ts",
                  "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                },
                {
                  "type": "file",
                  "path": "src/constants/status-codes.ts",
                  "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                }
              ]
            },
            "feature": {
              "files": [
                {
                  "type": "file",
                  "path": "src/shared/errors/api-error.ts",
                  "content": "import { STATUS_CODES, StatusCode } from \"../constants/status-codes\";\n\nexport class ApiError extends Error {\n  public readonly statusCode: StatusCode;\n  public readonly isOperational: boolean;\n  public readonly errors?: unknown;\n\n  constructor(\n    statusCode: StatusCode,\n    message: string,\n    errors?: unknown,\n    isOperational = true\n  ) {\n    super(message);\n    this.name = \"ApiError\";\n    this.statusCode = statusCode;\n    this.errors = errors;\n    this.isOperational = isOperational;\n\n    Error.captureStackTrace(this, this.constructor);\n  }\n\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\n  }\n\n  static unauthorized(message = \"Unauthorized\") {\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\n  }\n\n  static forbidden(message = \"Forbidden\") {\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\n  }\n\n  static notFound(message = \"Not Found\") {\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\n  }\n\n  static conflict(message = \"Conflict\") {\n    return new ApiError(STATUS_CODES.CONFLICT, message);\n  }\n\n  static server(message = \"Internal Server Error\") {\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\n  }\n}\n\n/*\n  ? Usage:\n  * throw new ApiError(404, \"Not found\");\n  * throw ApiError.badRequest(\"Bad request\");\n */\n"
                },
                {
                  "type": "file",
                  "path": "src/shared/constants/status-codes.ts",
                  "content": "export const STATUS_CODES = {\n  // 2xx Success\n  OK: 200,\n  CREATED: 201,\n  ACCEPTED: 202,\n  NO_CONTENT: 204,\n\n  // 3xx Redirection\n  MOVED_PERMANENTLY: 301,\n  FOUND: 302,\n  NOT_MODIFIED: 304,\n\n  // 4xx Client Errors\n  BAD_REQUEST: 400,\n  UNAUTHORIZED: 401,\n  FORBIDDEN: 403,\n  NOT_FOUND: 404,\n  CONFLICT: 409,\n  UNPROCESSABLE_ENTITY: 422,\n  TOO_MANY_REQUESTS: 429,\n\n  // 5xx Server Errors\n  INTERNAL_SERVER_ERROR: 500,\n  NOT_IMPLEMENTED: 501,\n  BAD_GATEWAY: 502,\n  SERVICE_UNAVAILABLE: 503,\n  GATEWAY_TIMEOUT: 504\n} as const;\n\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\n"
                },
                {
                  "type": "file",
                  "path": "src/modules/auth/auth.controller.ts",
                  "content": "import { ApiError } from \"../../shared/errors/api-error\";\n\n//? Not found\nexport const notFound = () => {\n  throw new ApiError(404, \"Not found\");\n};\n\n//? Bad request\nexport const badRequest = () => {\n  throw ApiError.badRequest(\"Bad request\");\n};\n\n//? Unauthorized\nexport const unauthorized = () => {\n  throw ApiError.unauthorized(\"Unauthorized\");\n};\n"
                }
              ]
            }
          }
        },
        "nextjs": {
          "env": [
            "NODE_ENV",
            "LOG_LEVEL"
          ],
          "dependencies": {
            "runtime": [
              "zod",
              "pino",
              "pino-pretty"
            ]
          },
          "architectures": {
            "file-api": {
              "files": [
                {
                  "type": "file",
                  "path": "src/utils/logger.ts",
                  "content": "import pino from \"pino\";\r\n\r\nconst isProduction = process.env.NODE_ENV === \"production\";\r\n\r\nexport const logger = pino({\r\n  level: process.env.LOG_LEVEL || \"info\",\r\n\r\n  base: {\r\n    pid: process.pid\r\n  },\r\n\r\n  timestamp: pino.stdTimeFunctions.isoTime,\r\n\r\n  formatters: {\r\n    level(label) {\r\n      return { level: label };\r\n    }\r\n  },\r\n\r\n  redact: {\r\n    paths: [\r\n      \"req.headers.authorization\",\r\n      \"req.headers.cookie\",\r\n      \"password\",\r\n      \"token\",\r\n      \"refreshToken\"\r\n    ],\r\n    censor: \"[REDACTED]\"\r\n  },\r\n\r\n  ...(isProduction\r\n    ? {}\r\n    : {\r\n        transport: {\r\n          target: \"pino-pretty\",\r\n          options: {\r\n            colorize: true,\r\n            translateTime: \"SYS:standard\",\r\n            ignore: \"pid,hostname\"\r\n          }\r\n        }\r\n      })\r\n});\r\n"
                },
                {
                  "type": "file",
                  "path": "src/utils/error-handler.ts",
                  "content": "import z, { ZodError } from \"zod\";\r\nimport { NextResponse } from \"next/server\";\r\nimport { ApiError } from \"@/utils/api-error\";\r\nimport { STATUS_CODES } from \"@/constants/status-codes\";\r\nimport { logger } from \"@/utils/logger\";\r\n\r\nexport function handleError(error: unknown) {\r\n  logger.error(error);\r\n  if (error instanceof ZodError) {\r\n    return ApiError.badRequest(\"Invalid request data\", z.flattenError(error));\r\n  }\r\n\r\n  if (error instanceof ApiError) {\r\n    return NextResponse.json(\r\n      {\r\n        success: false,\r\n        message: error.message,\r\n        statusCode: error.statusCode,\r\n        ...(Array.isArray(error.errors) && { errors: error.errors }),\r\n        ...(process.env.NODE_ENV === \"development\" && { stack: error.stack })\r\n      },\r\n      { status: error.statusCode }\r\n    );\r\n  }\r\n\r\n  return NextResponse.json(\r\n    {\r\n      success: false,\r\n      message: \"Internal Server Error\",\r\n      statusCode: STATUS_CODES.INTERNAL_SERVER_ERROR\r\n    },\r\n    { status: STATUS_CODES.INTERNAL_SERVER_ERROR }\r\n  );\r\n}\r\n"
                },
                {
                  "type": "file",
                  "path": "src/utils/api-response.ts",
                  "content": "import { NextResponse } from \"next/server\";\r\nimport { STATUS_CODES, StatusCode } from \"@/constants/status-codes\";\r\n\r\ntype ApiResponseParams<T> = {\r\n  success: boolean;\r\n  message: string;\r\n  statusCode: StatusCode;\r\n  data?: T | null;\r\n  errors?: unknown;\r\n};\r\n\r\nexport class ApiResponse<T = unknown> {\r\n  public readonly success: boolean;\r\n  public readonly message: string;\r\n  public readonly statusCode: StatusCode;\r\n  public readonly data?: T | null;\r\n  public readonly errors?: unknown;\r\n\r\n  constructor({\r\n    success,\r\n    message,\r\n    statusCode,\r\n    data = null,\r\n    errors\r\n  }: ApiResponseParams<T>) {\r\n    this.success = success;\r\n    this.message = message;\r\n    this.statusCode = statusCode;\r\n    this.data = data;\r\n    this.errors = errors;\r\n  }\r\n\r\n  send(): NextResponse {\r\n    return NextResponse.json(\r\n      {\r\n        success: this.success,\r\n        message: this.message,\r\n        statusCode: this.statusCode,\r\n        ...(this.data !== undefined && { data: this.data }),\r\n        ...(this.errors !== undefined && { errors: this.errors })\r\n      },\r\n      { status: this.statusCode }\r\n    );\r\n  }\r\n\r\n  static success<T>(\r\n    message: string,\r\n    data?: T,\r\n    statusCode: StatusCode = STATUS_CODES.OK\r\n  ): NextResponse {\r\n    return new ApiResponse<T>({\r\n      success: true,\r\n      message,\r\n      data,\r\n      statusCode\r\n    }).send();\r\n  }\r\n\r\n  static ok<T>(message = \"OK\", data?: T) {\r\n    return ApiResponse.success(message, data, STATUS_CODES.OK);\r\n  }\r\n\r\n  static created<T>(message = \"Created\", data?: T) {\r\n    return ApiResponse.success(message, data, STATUS_CODES.CREATED);\r\n  }\r\n\r\n  static error(\r\n    message = \"Error\",\r\n    errors?: unknown,\r\n    statusCode: StatusCode = STATUS_CODES.INTERNAL_SERVER_ERROR\r\n  ) {\r\n    return new ApiResponse({\r\n      success: false,\r\n      message,\r\n      errors,\r\n      statusCode\r\n    }).send();\r\n  }\r\n}\r\n/**\r\n * ? Usage:\r\nimport { ApiResponse } from \"@/utils/api-response\";\r\n\r\nexport async function GET() {\r\n  const data = { name: \"Akkal\" };\r\n\r\n  return ApiResponse.ok(\"Fetched successfully\", data);\r\n}\r\n*/\r\n"
                },
                {
                  "type": "file",
                  "path": "src/utils/api-error.ts",
                  "content": "import { STATUS_CODES, StatusCode } from \"@/constants/status-codes\";\r\n\r\nexport class ApiError extends Error {\r\n  public readonly statusCode: StatusCode;\r\n  public readonly isOperational: boolean;\r\n  public readonly errors?: unknown;\r\n\r\n  constructor(\r\n    statusCode: StatusCode,\r\n    message: string,\r\n    errors?: unknown,\r\n    isOperational = true\r\n  ) {\r\n    super(message);\r\n    this.name = \"ApiError\";\r\n    this.statusCode = statusCode;\r\n    this.errors = errors;\r\n    this.isOperational = isOperational;\r\n\r\n    if (Error.captureStackTrace) {\r\n      Error.captureStackTrace(this, this.constructor);\r\n    }\r\n  }\r\n\r\n  static badRequest(message = \"Bad Request\", errors?: unknown) {\r\n    return new ApiError(STATUS_CODES.BAD_REQUEST, message, errors);\r\n  }\r\n\r\n  static unauthorized(message = \"Unauthorized\") {\r\n    return new ApiError(STATUS_CODES.UNAUTHORIZED, message);\r\n  }\r\n\r\n  static forbidden(message = \"Forbidden\") {\r\n    return new ApiError(STATUS_CODES.FORBIDDEN, message);\r\n  }\r\n\r\n  static notFound(message = \"Not Found\") {\r\n    return new ApiError(STATUS_CODES.NOT_FOUND, message);\r\n  }\r\n\r\n  static conflict(message = \"Conflict\") {\r\n    return new ApiError(STATUS_CODES.CONFLICT, message);\r\n  }\r\n\r\n  static server(message = \"Internal Server Error\") {\r\n    return new ApiError(STATUS_CODES.INTERNAL_SERVER_ERROR, message);\r\n  }\r\n}\r\n"
                },
                {
                  "type": "file",
                  "path": "src/constants/status-codes.ts",
                  "content": "export const STATUS_CODES = {\r\n  // 2xx Success\r\n  OK: 200,\r\n  CREATED: 201,\r\n  ACCEPTED: 202,\r\n  NO_CONTENT: 204,\r\n\r\n  // 3xx Redirection\r\n  MOVED_PERMANENTLY: 301,\r\n  FOUND: 302,\r\n  NOT_MODIFIED: 304,\r\n\r\n  // 4xx Client Errors\r\n  BAD_REQUEST: 400,\r\n  UNAUTHORIZED: 401,\r\n  FORBIDDEN: 403,\r\n  NOT_FOUND: 404,\r\n  CONFLICT: 409,\r\n  UNPROCESSABLE_ENTITY: 422,\r\n  TOO_MANY_REQUESTS: 429,\r\n\r\n  // 5xx Server Errors\r\n  INTERNAL_SERVER_ERROR: 500,\r\n  NOT_IMPLEMENTED: 501,\r\n  BAD_GATEWAY: 502,\r\n  SERVICE_UNAVAILABLE: 503,\r\n  GATEWAY_TIMEOUT: 504\r\n} as const;\r\n\r\nexport type StatusCode = (typeof STATUS_CODES)[keyof typeof STATUS_CODES];\r\n"
                }
              ]
            }
          }
        }
      }
    }
  }
}
