Tetsu is an HTTP framework for Bun. Controllers get their dependencies as
function arguments, hooks run in fixed slots instead of a middleware chain,
and the compiler infers every type from the path to the handler. There are
no decorators, no DI container and no dependencies in the core.
: the method is
half of a route's identity, and an application that remembers its
routes — for a generated client, for tooling — needs to know which one
this is.
method: "GET",
path: "/users/:id"
Route path with :param segments, e.g. "/orders/:id/cancel".
Must start with /, contain no empty segments and no trailing slash;
a malformed literal is a compile error.
The endpoint logic; ctx is fully inferred, never annotate it.
The return type is inferred rather than demanded, and checked twice
over. Against the route's own contract, by the
HandlerResult
bound on R: answering with something response never declared is a
compile error that says so, instead of a structural diff against
Response. And against what the framework can serialize at all, by
the intersected
ValidateResult
: a stream handed over bare is
refused whether or not the route declared anything, because that is
the case no contract covers — without a response schema
HandlerResult is unknown and accepts every value there is.
handler: (
ctx: {
readonly out: Outgoing;
readonly req: Request & {
readonly cookies?: Bun.CookieMap;
};
readonly server: Bun.Server<unknown>;
readonly route: RouteInfo;
readonly startedAt: number;
readonly params: {
id: number;
};
}
ctx) => {
const
constuser:User|undefined
user=
repo: UserRepository
repo.find(
ctx: {
readonly out: Outgoing;
readonly req: Request & {
readonly cookies?: Bun.CookieMap;
};
readonly server: Bun.Server<unknown>;
readonly route: RouteInfo;
readonly startedAt: number;
readonly params: {
id: number;
};
}
ctx.
params: {
id: number;
}
params.id);
id: number
if (!
constuser:User|undefined
user) throwhttpError(404, "USER_NOT_FOUND");
return
constuser:User
user;
},
}),
}));
Bun.serve({ ...createApp({ routes: users({
repo: UserRepository
repo }) }) });
Nothing here is annotated. ctx.params.id is a number because the schema
converts the path segment, and without :id in the path it would not exist.
A request with a bad id gets a 422 and never reaches the handler.
Tetsu runs on Bun only. It has no plugin system: a package such as CORS or
rate limiting is a hook, mounted like your own. It has no container, no
file-based routing and no global registry: what runs for a route is
declared in code, on the route, its groups or the application.