API endpoints in KosmoJS are built using the defineRoute function, which provides a structured way to handle HTTP methods, apply middleware, and maintain type safety throughout your request-response cycle.
This system builds on Koa's middleware architecture while adding KosmoJS-specific enhancements for routing, validation, and type inference.
βΏ Basic Endpoint Structure β
Every API endpoint must export a route definition as its default export. You create this definition using the defineRoute function, which accepts a factory function that returns an array of middleware.
The factory function receives a single argument β an object containing HTTP method builders and the use function for custom middleware.
You typically destructure this object to access only the methods you need for that particular endpoint.
import { defineRoute } from "@front/{api}/users/[id]";
export default defineRoute(({ GET }) => [
GET(async (ctx) => {
// Handle GET requests
}),
]);This structure gives you a clear, declarative way to define which HTTP methods your endpoint supports. Each method handler receives the Koa context object, which KosmoJS enhances with additional properties to make common tasks easier.
β₯ Working with Multiple HTTP Methods β
Many endpoints need to handle multiple HTTP methods. You define handlers for each method you want to support by including them in the array returned from your factory function.
export default defineRoute(({ GET, POST, PUT, DELETE }) => [
GET(async (ctx) => {
// Retrieve resource
}),
POST(async (ctx) => {
// Create resource
}),
PUT(async (ctx) => {
// Update resource
}),
DELETE(async (ctx) => {
// Delete resource
}),
]);The order in which you list these handlers doesn't matter β KosmoJS routes requests to the appropriate handler based on the HTTP method of the incoming request.
If a request comes in for a method you haven't defined, KosmoJS automatically responds with a 405 Method Not Allowed status.
The available method builders are HEAD, OPTIONS, GET, POST, PUT, PATCH, and DELETE, covering all the standard HTTP methods you'll typically need in a REST API.
β οΈ Error Handling β
KosmoJS uses Koa's built-in error handling system, which provides a clean and flexible way to handle errors in your API endpoints.
Using ctx.throw() β
The primary way to return errors is using Koa's ctx.throw() method, which throws an HTTP error with a specific status code and message:
export default defineRoute(({ GET, DELETE }) => [
GET(async (ctx) => {
const user = await fetchUser(ctx.params.id);
if (!user) {
ctx.throw(404, "User not found");
}
ctx.body = user;
}),
DELETE(async (ctx) => {
const hasPermission = await checkPermission(ctx);
if (!hasPermission) {
ctx.throw(403, "Forbidden");
}
await deleteUser(ctx.params.id);
ctx.status = 204;
}),
]);Using ctx.assert() β
For cleaner validation logic, use ctx.assert() which throws an error when a condition is falsy. This is particularly useful for guard clauses and input validation:
export default defineRoute(({ GET }) => [
GET(async (ctx) => {
const { bookId } = ctx.params;
// Validate format - throws 400 if condition is false
const match = bookId.match(/^book-(\d+)$/);
ctx.assert(match, 400, "Invalid book ID format");
const id = match[1];
const book = await fetchBook(id);
// Throws 404 if book doesn't exist
ctx.assert(book, 404, "Book not found");
ctx.body = book;
}),
]);The ctx.assert(condition, status, message) pattern is especially useful for replacing verbose if-throw blocks with concise one-liners, making your validation logic easier to read.
Custom Error Handling β
By default, Koa returns plain text error messages. For API endpoints, you'll typically want to return JSON error responses instead. You can add custom error handling middleware to format errors consistently across your application.
For more information about implementing custom error handlers and Koa's error handling capabilities, refer to the Koa documentation.