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`);
},
});