Barazo AppView backend
barazo.forum
1// ---------------------------------------------------------------------------
2// Standard API error helpers
3// ---------------------------------------------------------------------------
4// Fastify's error handler checks `error.statusCode` to determine the HTTP
5// response code. These helpers create Error objects with the correct status
6// code and a structured message for consistent API responses.
7// ---------------------------------------------------------------------------
8
9import type { FastifyReply } from 'fastify'
10
11// ---------------------------------------------------------------------------
12// Shared OpenAPI error response schema
13// ---------------------------------------------------------------------------
14// All route files should import this instead of defining their own.
15// Matches the shape sent by the global error handler in app.ts and the
16// sendError helper below: { error, message, statusCode }.
17// ---------------------------------------------------------------------------
18
19export const errorResponseSchema = {
20 type: 'object' as const,
21 properties: {
22 error: { type: 'string' as const },
23 message: { type: 'string' as const },
24 statusCode: { type: 'integer' as const },
25 },
26}
27
28// ---------------------------------------------------------------------------
29// HTTP status text lookup
30// ---------------------------------------------------------------------------
31
32const HTTP_STATUS_TEXTS: Record<number, string> = {
33 400: 'Bad Request',
34 401: 'Unauthorized',
35 403: 'Forbidden',
36 404: 'Not Found',
37 409: 'Conflict',
38 429: 'Too Many Requests',
39 500: 'Internal Server Error',
40 502: 'Bad Gateway',
41}
42
43// ---------------------------------------------------------------------------
44// Structured error response helper
45// ---------------------------------------------------------------------------
46
47/**
48 * Send a structured error response with consistent shape: { error, message, statusCode }.
49 *
50 * - `error` – HTTP status text (e.g. "Bad Gateway")
51 * - `message` – human-readable description of the failure
52 * - `statusCode` – numeric HTTP status code
53 */
54export function sendError(reply: FastifyReply, statusCode: number, message: string) {
55 return reply.status(statusCode).send({
56 error: HTTP_STATUS_TEXTS[statusCode] ?? 'Error',
57 message,
58 statusCode,
59 })
60}
61
62/**
63 * Base API error with an HTTP status code.
64 * Fastify uses `statusCode` on thrown errors to set the response status.
65 */
66export class ApiError extends Error {
67 readonly statusCode: number
68
69 constructor(statusCode: number, message: string) {
70 super(message)
71 this.statusCode = statusCode
72 this.name = 'ApiError'
73 }
74}
75
76/**
77 * Create a 404 Not Found error.
78 *
79 * @param message - Human-readable description of what was not found.
80 */
81export function notFound(message: string): ApiError {
82 return new ApiError(404, message)
83}
84
85/**
86 * Create a 403 Forbidden error.
87 *
88 * @param message - Human-readable reason for the denial.
89 */
90export function forbidden(message: string): ApiError {
91 return new ApiError(403, message)
92}
93
94/**
95 * Create a 400 Bad Request error.
96 *
97 * @param message - Human-readable description of the validation failure.
98 */
99export function badRequest(message: string): ApiError {
100 return new ApiError(400, message)
101}
102
103/**
104 * Create a 409 Conflict error.
105 *
106 * @param message - Human-readable description of the conflict.
107 */
108export function conflict(message: string): ApiError {
109 return new ApiError(409, message)
110}
111
112/**
113 * Create a 429 Too Many Requests error.
114 *
115 * @param message - Human-readable description of the rate limit violation.
116 */
117export function tooManyRequests(message: string): ApiError {
118 return new ApiError(429, message)
119}