A lightweight Typescript wrapper around Hono. Get started with building secure APIs
The key features are:
- simplicity
- simplicity
- and yes... simplicity
Create a basic server:
import { HonoLite } from 'hono-lite';
const server = new HonoLite(PORT);
or with API key for security:
import { HonoLite } from 'hono-lite';
const server = new HonoLite(PORT, {
apiKeyValue: '....secret-api-key-goes-here....',
});
Want routes to share immutable objects? Easy:
import {HonoLite} from "hono-lite";
const db = new SomeSQLConnector(...);
const redis = new RedisConnector(...);
const extras = {db, redis}; // <-- these will now be delivered to every route callback
const server = new HonoLite(PORT, {
apiKeyValue: "....secret-api-key-goes-here...."
}, extras);
Just return an object (like you normally do!):
server.Get('/fetch-some-obj', (cxt, extras) => {
//... do something here ...
return {
status: 'ok',
meta: extras['key'],
};
});
Just throw an instance of ErrorLite
; just give it an id
and a body
:
throw new ErrorLite('schema_validation_error', "'input' must be a valid string"); //default status 500
Want something richer or with a custom status code in the error? Do this:
throw new ErrorLite(
'auth_error',
{
message: 'unauthorized access',
someKey: '... more metadata ...',
},
401,
);
API servers, need API keys :) Easy peasy:
const server = new HonoLite(
PORT,
{ apiKeyValue: API_KEY }, // <--- checked against "x-api-key" req header by default
);
Want a custom request header? Do this:
const server = new HonoLite(PORT, {
apiKeyValue: API_KEY,
apiKeyHeader: 'my-custom-header',
});
For instance, if you need to define custom middleware, good ol' Hono-style, do this:
server.use(async (_, next) => {
//... do something ...
await next();
});
Want to handle 404s differently, do this:
server.notFound((cxt) => {
return cxt.json({ message: "can't find what you're looking for" }, 404);
});
The server
is basically an extended instance of Hono.
Here's a basic server built using HonoLite.
import { HonoLite, ErrorLite } from 'hono-lite';
// define port, API key is optional
const PORT = 15000;
const API_KEY = 'some-api-key-goes-in-here';
// any object can be sent into the server as an "extra"
// and retrieved later inside the scope of a route callback
const xtra = {
key: 'some-stock-value',
db: 'could-be-an-object-too',
};
// instantiate the server with all these parameters
const server = new HonoLite(PORT, { apiKeyValue: API_KEY }, xtra);
// define a GET route
// each route could also be its own module that exports
// the callback function
server.Get('/posts/:id', (cxt, extras) => {
const { id = '' } = cxt.req.param();
//... do something here ...
return {
message: `You wanted id: ${id}`,
meta: extras.db,
};
});
// define a POST route that is secured using an API key
// the API key must be found in the 'x-api-key' req header
server.Post(
'/do-some-thing-securely',
async (cxt) => {
// read the request body, reject if malformed
const body = await cxt.req.json().catch(() => {
throw new ErrorLite('invalid_body', 'request body was malformed');
});
// extract input data from the body
// validate its schema
const { input = '' } = body;
if (typeof input === typeof '' && input.trim().length > 0) {
//... do something ...
return {
status: 'success',
meta: `You sent: ${input}`,
};
} else
throw new ErrorLite('schema_validation_error', {
message: "'input' must be a valid string",
});
},
{ useApiKey: true },
);
// start the server, log success message
server
.start()
.then((port) => console.log(`Server is running on port: ${port}`))
.catch((reason) => server.close(() => console.log(reason)));
// cleanup before exit
const cleanup = () => server.close(() => console.log('Server closed'));
process.on('exit', cleanup);
process.on('SIGINT', cleanup);