REST API + typed client
CRUD routes, export the server type, and call it from another module without codegen.
Prerequisites
deno add jsr:@webtools/expressapi- Keep the server importable for types - guard
listen()withimport.meta.main
Server
// server.ts
import { HttpServer, z } from "jsr:@webtools/expressapi";
export const server = new HttpServer()
.get("/users", (_req, res) =>
res.json({ users: [] as { id: string; name: string }[] }))
.get("/users/:id", (req, res) => {
const user = findUser(req.params.id);
if (!user) return res.json({ found: false as const });
return res.json({ found: true as const, name: user.name });
})
.post("/users", (req, res) =>
res.status(201).json({ id: "1", name: req.body.name }), [], {
body: z.object({
name: z.string().min(3),
email: z.string().email(),
}),
})
.put("/users/:id", (req, res) =>
res.json({ updated: true, id: req.params.id }))
.delete("/users/:id", (_req, res) => res.status(204).send(null));
export type AppRouter = typeof server;
if (import.meta.main) server.listen(5050);Client
// client.ts
import type { AppRouter } from "./server.ts";
import { HttpClient } from "jsr:@webtools/expressapi";
const client = new HttpClient<AppRouter>({ baseUrl: "http://localhost:5050" });
const users = await client.get("/users");
const user = await client.get("/users/:id", { params: { id: "1" } });
if (user.found) console.log(user.name);
const created = await client.post("/users", {
body: { name: "Charlie", email: "c@example.com" },
});
await client.delete("/users/:id", { params: { id: "1" } }); // nullRelated: Typed client, Routing.