The handler

The createKaitoHandler function is a very minimal part of Kaito, it simply wraps all your functions together and returns a request -> response function.

import {createKaitoHandler} from '@kaito-http/core';
 
const handler = createKaitoHandler({
	getContext,
	router,
	onError,
});
 
Bun.serve({fetch: handler, port: 3000});

onError

In the example above, you can see I have included a property called onError. This is a function that is called whenever an error is thrown in the request lifecycle. This function should reply with an object that contains a status and message. These will be used to reply to the client.

import {createKaitoHandler} from '@kaito-http/core';
import {ZodError} from 'zod';
 
const handler = createKaitoHandler({
	// Be careful with using `res` here.
	onError: async ({error, req, res}) => {
		if (error instanceof ZodError) {
			return {status: 400, message: 'Invalid request'};
		}
 
		return {status: 500, message: 'Internal Server Error'};
	},
	// ...
});

Before/After

Kaito has a concept of before and after hooks, which are executed before and after the router. This is useful for things like logging etc. Personally, I use it to add CORS headers.

const server = createServer({
	getContext,
	router,
	onError: async ({error, req, res}) => {
		// ...
	},
	// Before runs code before every request. This is helpful for setting things like CORS.
	// You can return a value from before, and it will be passed to the after call.
	// If you end the response in `before`, the router will not be called.
	before: async (req, res) => {
		res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
		res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
		res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
		res.setHeader('Access-Control-Max-Age', '86400');
		res.setHeader('Access-Control-Allow-Credentials', 'true');
 
		if (req.method === 'OPTIONS') {
			res.statusCode = 204;
			// This is safe, because the router will know that the response is ended.
			res.end();
		}
 
		// Return something from `before`, and it will be passed to `after`.
		return {
			now: Date.now(),
		};
	},
 
	// Access the return value from `before` in `after`.
	// If the before function ends the response, this *will* be called!
	// So be careful about logging request durations etc
	after: async ({now}) => {
		console.log(`Request took ${Date.now() - now}ms`);
	},
});