Skip to Content

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

Terminal
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:

src/bookmark.ts (add to the bottom)
/** 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

src/06-server.ts
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:

  1. typia.createValidate<BookmarkCreate>() generates a reusable validator that also implements the Standard Schema  interface. @hono/typia-validator consumes it directly.
  2. c.req.valid("json") returns the typed input. TypeScript knows it’s BookmarkCreate because the middleware narrowed it — there’s no as BookmarkCreate cast in your handler.
  3. The validator runs before your handler, so by the time service.add(input) is called, every constraint on BookmarkCreate (URL format, tag count, lengths, …) has already passed. If anything’s wrong, Hono returns a structured error response automatically.

Run it

For Node:

Terminal
npm i @hono/node-server
src/06-run.ts
import { 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}`);
Terminal
npx ttsx src/06-run.ts

Then in another terminal:

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:

Terminal
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:

FrameworkIntegrationWhat you write
NestJS@nestia/core@TypedBody() input: BookmarkCreate instead of a DTO class
tRPCDirect typia.input(typia.createAssert<BookmarkCreate>()) on the procedure
MCP server@typia/mcpregisterMcpControllers({ server, controllers: [typia.llm.controller<BookmarkService>("bm", service)] })
Vercel AI@typia/verceltoVercelTools({ controllers: [...] })
LangChain@typia/langchaintoLangChainTools({ 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 Bookmark type 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.

→ Step 7: Next steps

Last updated on