Step 6 — Plug into a framework
The bookmark service is now usable from typia, from a CLI script, and from an LLM. The last missing piece is a normal HTTP endpoint so any client can hit it. We’ll use Hono — small, fast, runs on Node / Bun / edge — but the typia parts of this step transfer to NestJS, tRPC, or Express directly.
The point is not “how to write HTTP routes.” The point is: the same Bookmark type drives validation in every layer. No DTO duplication, no separate request schema, no manual if (!body.url) throw ….
Install
npm i hono @hono/typia-validator@hono/typia-validator is the Hono integration. It accepts a validator built with typia.createValidate and runs it on the request body before your handler sees it.
A typed request payload
We don’t want clients to send us id or createdAt — those are server-generated. Let’s express that as a derived type:
/** What a client sends when creating a new bookmark. */
export type BookmarkCreate = Omit<Bookmark, "id" | "createdAt">;Omit is plain TypeScript — typia reads it and produces a validator for exactly the right shape.
The HTTP server
import { Hono } from "hono";
import { typiaValidator } from "@hono/typia-validator";
import typia from "typia";
import { BookmarkCreate } from "./bookmark";
import { BookmarkService } from "./BookmarkService";
const service = new BookmarkService();
const validateCreate = typia.createValidate<BookmarkCreate>();
const app = new Hono();
// POST /bookmarks — create a new bookmark
app.post(
"/bookmarks",
typiaValidator("json", validateCreate),
async (c) => {
const input = c.req.valid("json"); // typed as BookmarkCreate
const { id } = await service.add(input);
return c.json({ id }, 201);
},
);
// GET /bookmarks?tag=docs
app.get("/bookmarks", async (c) => {
const tag = c.req.query("tag") ?? "";
const result = await service.list({ tag });
return c.json(result);
});
export default app;Three things to notice:
typia.createValidate<BookmarkCreate>()generates a reusable validator that also implements the Standard Schema interface.@hono/typia-validatorconsumes it directly.c.req.valid("json")returns the typed input. TypeScript knows it’sBookmarkCreatebecause the middleware narrowed it — there’s noas BookmarkCreatecast in your handler.- The validator runs before your handler, so by the time
service.add(input)is called, every constraint onBookmarkCreate(URL format, tag count, lengths, …) has already passed. If anything’s wrong, Hono returns a structured error response automatically.
Run it
For Node:
npm i @hono/node-serverimport { serve } from "@hono/node-server";
import app from "./06-server";
const PORT = 3000;
serve({ fetch: app.fetch, port: PORT });
console.log(`Listening on http://localhost:${PORT}`);npx ttsx src/06-run.tsThen in another terminal:
curl -s -X POST http://localhost:3000/bookmarks \
-H 'content-type: application/json' \
-d '{ "url": "https://typia.io", "title": "typia", "tags": ["docs"] }'
# → {"id":"…uuid…"}
curl -s http://localhost:3000/bookmarks?tag=docs
# → {"items":[…]}Try a bad payload:
curl -s -X POST http://localhost:3000/bookmarks \
-H 'content-type: application/json' \
-d '{ "url": "lol", "title": "", "tags": [] }'You get a 400 carrying the structured errors — $input.url, $input.title, $input.tags — with the same expected strings you saw in step 3. The HTTP layer never gets a chance to insert a malformed bookmark.
What other frameworks look like
The patterns are short — see the dedicated pages for full examples:
| Framework | Integration | What you write |
|---|---|---|
| NestJS | @nestia/core | @TypedBody() input: BookmarkCreate instead of a DTO class |
| tRPC | Direct typia | .input(typia.createAssert<BookmarkCreate>()) on the procedure |
| MCP server | @typia/mcp | registerMcpControllers({ server, controllers: [typia.llm.controller<BookmarkService>("bm", service)] }) |
| Vercel AI | @typia/vercel | toVercelTools({ controllers: [...] }) |
| LangChain | @typia/langchain | toLangChainTools({ controllers: [...] }) |
The same BookmarkService class plugs into all five LLM-shaped surfaces; the same BookmarkCreate type plugs into all the HTTP-shaped ones.
What you’ve done
- Stood up a real HTTP server backed by
BookmarkService - Reused the
Bookmarktype for HTTP request validation — no DTO class - Returned structured 400 errors for bad input, for free
- Seen the same service work from CLI, from HTTP, and from an LLM
That’s the whole loop. One TypeScript type — Bookmark — drives validation, JSON parsing, schema generation, HTTP middleware, and LLM tool descriptions.