Getting started
Write a first route, add a service, and run it on Bun or Node.
Install
bun add @sukujs/core @sukujs/platform-bun zodAny Standard Schema library works in place of Zod. Use
@sukujs/platform-node when you deploy on Node. Packages are published under the
@sukujs scope on npm; the current release is 0.1.0.
A first route
import { Suku, route } from '@sukujs/core';
import { bunAdapter } from '@sukujs/platform-bun';
import { z } from 'zod';
const hello = route.get('/hello/:name', {
query: z.object({ shout: z.coerce.boolean().default(false) }),
handle: ({ params, query }) => {
const text = `Hello, ${params.name}`;
return { message: query.shout ? text.toUpperCase() : text };
},
});
const app = await Suku.create({ routes: [hello], adapter: bunAdapter() });
const server = await app.listen(3000);
console.log(`listening on ${server.url}`);bun run src/main.ts
curl 'localhost:3000/hello/ada?shout=true'
# {"message":"HELLO, ADA"}This style needs no decorators and no compiler options.
Add a service and a routes class
Classes that receive services through their constructor need TypeScript's decorator metadata.
UsersService below takes nothing in its constructor, so it needs no decorator at all.
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}import { NotFoundException, Routes, feature, route, singleton } from '@sukujs/core';
import { z } from 'zod';
export class UsersService {
private readonly users = new Map([[1, { id: 1, name: 'Ada Lovelace' }]]);
find(id: number) {
return this.users.get(id);
}
}
@Routes('/users')
export class UsersRoutes {
constructor(private readonly users: UsersService) {}
findOne = route.get('/:id', {
params: z.object({ id: z.coerce.number().int() }),
handle: ({ params }) => {
const user = this.users.find(params.id);
if (!user) throw new NotFoundException(`User ${params.id} not found`);
return user;
},
});
}
export const UsersFeature = feature('users', {
routes: [UsersRoutes],
services: [singleton(UsersService)],
});const app = await Suku.create({ features: [UsersFeature], routes: [hello], adapter: bunAdapter() });Turn off type-only import rewriting
Lint rules that rewrite imports used only as types into import type erase the class that
constructor injection needs. Disable Biome's style/useImportType and ESLint's
consistent-type-imports. Suku reports the problem at startup as an
UnresolvableParameterError naming the parameter.
Run it
Bun runs TypeScript and emits decorator metadata itself.
bun run src/main.ts