Byte
A simple, performance-focused web framework that works on Bun, Deno, Cloudflare Workers and browsers.
import { Byte, send } from '@bit-js/byte';
export default new Byte()
.get('/', () => send.body('Hi'));
Concepts
Context
Context is an object represents the current request.
ctx.path
: The parsed request pathname, doesn't have a slash at the start.ctx.pathStart
: The start index of the pathname in the full URL string (req.url
). This property can be useful for subdomain matching.ctx.pathEnd
: The end index of the path. When query parameters are presented, path end is set to the start of the query, otherwise it is set to the length of the URL.ctx.params
: The parsed URL parameters. Defaults to undefined
with static routes.ctx.state
: This property stores validators result. Defaults to undefined
with routes that have no validators.ctx.req
: The original request object.
Handler
Handlers are functions that accepts the request context as parameter and return a Response
object.
() => new Response('Hi');
(ctx) => new Response(ctx.params.id);
If your handler doesn't use the request context, in certain cases the router compiler will optimize the matcher to not pass in the request context as a parameter.
Defining routes
You can define routes with the syntax method(path, handler)
.
new Byte()
.get('/', () => new Response('Hi'))
.put('/user/:id', (ctx) => new Response(ctx.params.id))
.any('/*', () => new Response('Not found'));
Route patterns
Parametric and wildcard patterns are supported.
'/:id'
'/user/:id/name/:name'
'/nav/*'
'/user/:id/*'
Like other frameworks, Byte pass the parsed parameter values to ctx.params
, but wildcard parameter is named $
instead of *
.
Fallback
Use fallback
to handle the request when the path does not match any registed route.
new Byte()
.get('/', () => new Response('Hi'))
.fallback((ctx) => new Response(ctx.path));
Validators
Validators are functions that parse and validate incoming request data.
new Byte()
.get('/', {
body: async (ctx) => {
try {
return await ctx.req.text();
} catch (_) {
return new Response('Something went wrong');
}
}
}, ctx => new Response(ctx.state.body));
Parsed data is passed into ctx.state
as a property.
If a Response
object is returned from the validator, it will be used instead of the handler response.
If the validator returns a Promise
it should be properly marked as async
for the compiler to detect.
The validator name must be a valid JavaScript variable name.
Actions
Actions are functions that executes before validators.
new Byte()
.action((ctx) => {
})
.action((ctx) => {
});
If a Response
object is returned from any action function, it will be used directly.
Plugins
Plugins are objects with a plug()
function that modify the app instance.
import type { Plugin } from '@bit-js/byte';
const plugin = {
plug(app) {
};
};
new Byte().use(plugin);
Plugins are meant to be used by third party libraries to add functionalities.
Client
Byte provides a client implementation with type inference.
To use it, first export the type of your app.
const app = new Byte()
.get('/', () => send.body('Hi'))
.get('/user/:id', (ctx) => send.body(ctx.params.id))
.post('/text', {
body: async ctx => await ctx.req.text()
}, (ctx) => send.body(ctx.state.body))
export type App = typeof app;
Then use it in client code.
import type { App } from './app';
import { bit } from '@bit-js/byte';
const app = bit<App>('http://localhost:3000');
const msg = await app.get('/');
await msg.text();
const id = await app.get('/user/:id', {
params: { id: 90 }
});
await msg.text();
const body = await app.post('/text', {
body: 'Hi'
});
await body.text();
You can also pass in a custom fetch
function.
const app = bit<App>('http://localhost:3000', myFetch);
Utilities
Sending response
Response utilities should be used for client type inference.
import { send } from '@bit-js/byte';
send.body('Hi');
send.text('Hi'):
send.json({ hello: world });
send.link('/home', 302);
send.events(readable);
All response utilities (except send.link
) has two arguments.
send(body: any, init?: ResponseInit): any;
If a ResponseInit
is passed in it will be merged with the corresponding headers.
send.body('Not found', { status: 404 });
Body parsers
Use body parsers to parse request body and handle errors.
import { parse } from '@bit-js/byte';
app.post('/text', {
body: parse.text({
then(data) {
},
catch(error) {
}
})
});
Available parsers are:
parse.text
: Parse request body as text.parse.json
: Parse request body as JSON.parse.blob
: Parse request body as Blob
.parse.form
: Parse request body as FormData
.parse.buffer
: Parse request body as ArrayBuffer
.
Query parsers
Use query parsers to get parameter values out of a query string.
import { query } from '@bit-js/byte';
const getID = query.value('id');
const getCats = query.values('category');
const result = query.get(ctx);
The query parsers (except query.get
) utils do not decodeURIComponent
the result by default.
CORS
Set CORS headers on every requests.
import { cors } from '@bit-js/byte';
app.action(cors());
app.action(cors(options));
Available options are:
interface CORSOptions {
allowOrigin?: string;
allowMethods?: string | string[];
exposeHeaders?: string | string[];
maxAge?: number;
allowCredentials?: boolean;
allowHeaders?: string | string[];
}
CSRF
A CSRF protection layer by checking request origin.
import { csrf } from '@bit-js/byte';
app.action(csrf());
app.action(csrf(options));
Available options are:
interface CSRFOptions {
origins?: string;
verify?: (origin: string) => boolean;
fallback?: (ctx: BaseContext) => any;
}