: 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"
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.
path: "/users",
handler: (ctx: {
readonlyreq:Request& {
readonlycookies?:Bun.CookieMap;
};
readonlyserver:Bun.Server<unknown>;
readonlyout:Outgoing;
readonlyroute:RouteInfo;
readonlystartedAt:number;
readonlyparams: {};
}) => User[]
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: () => [...
users: Map<number, User>
users.values()],
}),
get: route({
method: "GET"
The method this route answers.
Kept as a literal rather than widened to
Method
: 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.
path: "/users/:id",
schema: { params: UserId },
handler: (ctx: {
readonlyout:Outgoing;
readonlyreq:Request& {
readonlycookies?:Bun.CookieMap;
};
readonlyserver:Bun.Server<unknown>;
readonlyroute:RouteInfo;
readonlystartedAt:number;
readonlyparams: {
id:number;
};
}) => User
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=
users: Map<number, User>
users.get(
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: number
id);
if (!
constuser:User|undefined
user) throwhttpError(404, "USER_NOT_FOUND", "No such user");
return
constuser:User
user;
},
}),
create: route({
method: "POST"
The method this route answers.
Kept as a literal rather than widened to
Method
: 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: "POST",
path: "/users"
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.
path: "/users",
schema: { body: NewUser },
handler: (
ctx: {
readonly params: {};
readonly out: Outgoing;
readonly req: Request & {
readonly cookies?: Bun.CookieMap;
};
readonly server: Bun.Server<unknown>;
readonly route: RouteInfo;
readonly startedAt: number;
readonly body: {
name: string;
email: string;
};
}
ctx) => {
const
constuser: {
name:string;
email:string;
id:number;
}
user= {
id: number
id:
users: Map<number, User>
users.
size: number
@returns ― the number of elements in the Map.
size+1, ...
ctx: {
readonly params: {};
readonly out: Outgoing;
readonly req: Request & {
readonly cookies?: Bun.CookieMap;
};
readonly server: Bun.Server<unknown>;
readonly route: RouteInfo;
readonly startedAt: number;
readonly body: {
name: string;
email: string;
};
}
ctx.
body: {
name: string;
email: string;
}
body };
users: Map<number, User>
users.set(
constuser: {
name:string;
email:string;
id:number;
}
user.
id: number
id,
constuser: {
name:string;
email:string;
id:number;
}
user);
ctx: {
readonly params: {};
readonly out: Outgoing;
readonly req: Request & {
readonly cookies?: Bun.CookieMap;
};
readonly server: Bun.Server<unknown>;
readonly route: RouteInfo;
readonly startedAt: number;
readonly body: {
name: string;
email: string;
};
}
ctx.
out: Outgoing
Response parameters for the serialized handler result.
out.
status: number |undefined
status=201;
return
constuser: {
name:string;
email:string;
id:number;
}
user;
},
}),
}));
ctx.params.id is a number: the schema converts the :id segment before
the handler runs, and /users/abc never reaches it.
ctx.body exists only on create, the route with a body schema, and has
the schema’s type.
httpError(404, "USER_NOT_FOUND", …) is your own error, in the same shape
as the framework’s.
The body is parsed as JSON, the route’s default, whatever content-type
the client sends (curl’s -d sends a form type). A request the schema
refuses gets a 422 with every issue at once:
Routing is Bun’s, and only a real socket reaches it. serve() from
@tetsujs/core/testing starts the application on a free port and stops it
when the test file finishes: