@contract-kit/errors
v0.1.9
Published
Error catalog and error handling utilities for contract-kit
Maintainers
Readme
@contract-kit/errors
Error catalog and error handling utilities for contract-kit.
Installation
npm install @contract-kit/errors
# or
bun add @contract-kit/errorsTypeScript Requirements
This package requires TypeScript 5.0 or higher for proper type inference.
Features
- Error Catalog: Define your application's errors in a central location with HTTP status codes
- AppError Class: A throwable error class that domain and application code can use
- Error Factory: Type-safe factory for creating errors from your catalog
- Response Schemas: Optionally colocate Standard Schema response bodies with catalog entries
- Server Integration:
AppErrorinstances map to standard HTTP error responses in the server runtime
Usage
Define Your Error Catalog
// app/errors.ts
import { defineErrors, createErrorFactory } from "@contract-kit/errors";
import { z } from "zod";
const TodoNotFoundResponse = z.object({
code: z.literal("TODO_NOT_FOUND"),
message: z.string(),
details: z.object({ id: z.string() }),
});
export const errors = defineErrors({
TodoNotFound: {
code: "TODO_NOT_FOUND",
status: 404,
message: "Todo not found",
responseSchema: TodoNotFoundResponse,
},
Unauthorized: {
code: "UNAUTHORIZED",
status: 401,
message: "You must be signed in",
},
BadRequest: {
code: "BAD_REQUEST",
status: 400,
message: "Invalid request",
},
});
export const err = createErrorFactory(errors);The optional responseSchema field accepts any Standard Schema-compatible validator. It lets contracts reuse the same route-owned error schema without making the error package depend on a specific schema library:
export const getTodo = todos.get("/api/todos/:id").responses({
200: TodoSchema,
404: errors.TodoNotFound.responseSchema,
});Throw Errors in Your Domain/Application Code
// application/todos/getTodo.ts
import { err } from "@/app/errors";
export async function getTodo(id: string) {
const todo = await db.todos.findById(id);
if (!todo) {
throw err.appError("TodoNotFound", { details: { id } });
}
return todo;
}Next.js Integration
AppError instances thrown from handlers or use cases are mapped by the server runtime to standard JSON responses with the appropriate HTTP status code. Because handler responses are contract-validated, declare any expected AppError statuses in the route contract.
// lib/server.ts
import { createNextServer } from "@contract-kit/next";
export const server = await createNextServer({
ports: {},
createContext: async ({ req }) => ({
requestId: req.headers.get("x-request-id") ?? crypto.randomUUID(),
}),
// Optional: handle truly unexpected errors
onError: ({ err, req, ctx }) => {
console.error("Unexpected error:", err);
return { status: 500, body: { message: "Internal server error" } };
},
});Error Response Format
When an AppError is thrown, it will be converted to a standard JSON response:
{
"code": "TODO_NOT_FOUND",
"message": "Todo not found",
"details": { "id": "abc123" }
}The HTTP status code is taken from the error definition in the catalog.
Framework-owned server errors use the same envelope and may include a top-level requestId when one is available from context:
{
"code": "INTERNAL_SERVER_ERROR",
"message": "Internal server error",
"requestId": "req_123"
}HTTP Error Helpers
You can start from a shared catalog of common HTTP errors:
import { defineErrors } from "@contract-kit/errors";
import { httpErrors } from "@contract-kit/errors/http";
export const errors = defineErrors({
...httpErrors,
TodoNotFound: {
code: "TODO_NOT_FOUND",
status: 404,
message: "Todo not found",
},
});The httpErrors catalog includes:
BadRequest(400)Unauthorized(401)Forbidden(403)NotFound(404)Conflict(409)UnprocessableEntity(422)InternalServerError(500)
API Reference
defineErrors<T>(defs: T): T
Type-preserving helper to define an error catalog. Each error may include an optional responseSchema Standard Schema for route-owned contract responses.
createErrorFactory<T>(catalog: T): ErrorFactory<T>
Create an error factory bound to a specific catalog.
AppError
Error class that can be thrown from domain/application code.
isAppError(err: unknown): boolean
Type guard to check if an error is an AppError.
toErrorResponseBody(err: AppError): ErrorResponseBody
Convert an AppError to a standard error response body with { code, message, details }.
createErrorResponseBody(args): ErrorResponseBody
Create a standard error response body with { code, message, details?, requestId? }.
isErrorResponseBody(value: unknown): value is ErrorResponseBody
Type guard for Contract Kit's standard error response envelope.
httpErrors
A base catalog of common HTTP-related errors. See HTTP Error Helpers section above.
HttpErrorCatalog
Type of the default HTTP error catalog.
License
MIT
