seahaven-ap/packages/api/src/http.ts

57 lines
1.3 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type { Context } from "hono";
import type { ContentfulStatusCode } from "hono/utils/http-status";
export type ErrorCode =
| "UNAUTHENTICATED"
| "FORBIDDEN"
| "NOT_FOUND"
| "VALIDATION_ERROR"
| "CONFLICT"
| "INTERNAL_ERROR"
| "DATABASE_UNAVAILABLE";
export class ApiError extends Error {
constructor(
readonly status: ContentfulStatusCode,
readonly code: ErrorCode,
message: string,
) {
super(message);
this.name = "ApiError";
}
}
export const CORRELATION_HEADER = "x-correlation-id";
export type ErrorEnvelope = {
error: {
code: ErrorCode;
message: string;
correlationId: string;
};
};
export function correlationIdFrom(c: Context): string {
const raw = c.req.header(CORRELATION_HEADER)?.trim();
if (raw && raw.length <= 128) {
return raw;
}
return randomUUID();
}
export function errorBody(code: ErrorCode, message: string, correlationId: string): ErrorEnvelope {
return { error: { code, message, correlationId } };
}
export function errorJson(
c: Context,
status: ContentfulStatusCode,
code: ErrorCode,
message: string,
correlationId?: string,
) {
const id = correlationId ?? correlationIdFrom(c);
c.header(CORRELATION_HEADER, id);
return c.json(errorBody(code, message, id), status);
}