Getting started
Build a tiny DaloyJS server, hit it with the typed client, and inspect the OpenAPI spec, in five minutes.
- 01Scaffoldpnpm add @daloyjs/core zod
- 02Write a routeapp.route({ ... })
- 03Add OpenAPI & docs UIdocs: true
- 04Call the typed clientcreateClient(app)
- 05Generate a Hey API SDKpnpm exec openapi-ts
1. Scaffold
We use node --import tsx so TypeScript entrypoints run directly during local development without a separate build step.
We use src/index.ts and --watch here so the layout matches what create-daloy emits, copy/paste between this guide and a scaffolded project without renaming files.
2. Write your first route
Prefer the colorized startup panel you get from create-daloy templates? Swap the plain console.log for printStartupBanner() from @daloyjs/core/banner: it renders a TTY-aware, ASCII-fallback boxed banner with your app name, URL, and any extra links (API docs, health check, etc.):
Don't want to spin up a real server? Every App exposes app.request(input, init?), an in-process test client that takes a URL or Request and returns a Response , no network stack, no port, no second terminal. It's the same entrypoint the typed client and testing guide use:
3. Add OpenAPI & docs UI
One line on the App constructor and DaloyJS auto-mounts GET /openapi.json + GET /openapi.yaml (the live spec in both formats) and GET /docs (a Scalar API reference UI) for you:
Open http://localhost:3000/docs for an interactive Scalar reference, http://localhost:3000/openapi.json for the raw JSON spec, or http://localhost:3000/openapi.yaml for the YAML spec.
If you omit openapi.info entirely, DaloyJS will read your project's package.json (name, version, description) and use those for the spec automatically. Deno projects without a package.json fall back to deno.json / deno.jsonc. Explicit values always override the autofill.
Prefer a factory call? createApp(options) is an exported alias of new App(options) with identical behaviour:
Prefer the classic Swagger UI?
Scalar is the default because it's faster, prettier, and friendlier on mobile, but if your team is used to Swagger UI, or you have existing screenshots, runbooks, or muscle memory built around it, DaloyJS ships it out of the box. Flip the ui field on the object form of docs and you're back to the familiar green UI:
Same URL (GET /docs), same live /openapi.json and /openapi.yaml endpoints, same CSP handling, only the rendered HTML changes. You can also keep both: leave the auto-mounted route on Scalar and expose a second Swagger route yourself with swaggerUiHtml() (see the OpenAPI guide for the manual recipe).
Want a custom path? Use the object form: docs: { ui: "swagger", path: "/reference" }. Want it only in development? Use docs: "auto": it skips the mount when production: true. Need full control? Set docs: false and mount your own routes with generateOpenAPI() and swaggerUiHtml() / scalarHtml(): see the OpenAPI guide.
Both swaggerUiHtml() and scalarHtml() load their default assets from the jsDelivr CDN, so a strict Content-Security-Policy must allow those assets or the docs UI can render blank. The auto-mounted route and htmlResponse() both add a compatible CSP automatically; if you build your own response, import docsContentSecurityPolicy from @daloyjs/core/docs and pass the result as the response header:
4. Use the typed in-process client
The client's methods are inferred from the routes you chained onto app. Keep the registrations chained (new App().route(a).route(b)) and avoid a widening const app: App annotation; either one erases the per-route types and the client falls back to an untyped surface.
5. Generate a Hey API SDK
For consumers outside the monorepo, generate a fully typed fetch SDK:
Keep the dev server from step two running, then write the live OpenAPI document to disk before you run the SDK generator: