Architecture
How VitNode combines TanStack Start for UI and isomorphic routing with Hono for API authorization and database access.
A VitNode app is two layers in one project. TanStack Start renders the pages, and a Hono API answers every request for data. Read this page to know which layer owns a job before you write code for it.
- TanStack Start: the UI, server-side rendering (SSR), routing and the client cache.
- Hono API: API routes, sessions, staff permissions and database access.
Who owns what
| Responsibility | TanStack Start (web app) | Hono (API) |
|---|---|---|
| Routing | Page URLs, dynamic parameters, nested layouts | API routes under /api/* |
| Data fetching | Route loaders and the universal fetcher | Queries through Drizzle ORM |
| State & cache | TanStack Query client cache | Redis domain cache and the database |
| Security | UI guards (redirecting signed-out visitors) | Enforces authentication, permissions, CSRF and rate limits |
Route guards (beforeLoad) improve the experience by redirecting visitors
early, but anyone can call the API directly. Every private endpoint checks the
session cookie and permissions on every request.
Request flow
When a visitor opens a page such as /blog:
| Step | Runs on | What happens |
|---|---|---|
| 1. Request | Browser | The visitor navigates to /blog |
| 2. Routing | Server (SSR) / browser | TanStack Router matches the route and runs its loader |
| 3. Query warming | Server / browser | context.queryClient.query runs the query, which calls the fetcher |
| 4. API call | Server / browser | The universal fetcher calls the Hono endpoint by plugin id |
| 5. API middleware | Server (Hono) | Reads the session cookie, applies rate limits, provides c.get("db") |
| 6. Handler | Server (Hono) | The route validates its input and queries PostgreSQL through Drizzle |
| 7. Response | Server / browser | The JSON fills the TanStack Query cache and the component renders |
Request middleware
Before a page route matches, every request passes through the middleware that
createVitNodeStart installs in src/start.ts, in this order:
- CSRF, for server function calls.
- Locale, for page requests: canonical
308redirects, the locale cookie, and, on HTML responses,Cache-Control: private, no-storeplus the security headers. - Your own
requestMiddleware, in the order you list it.
An app cannot put its own middleware in front of the first two. /api/*
passes through untouched, so the Hono API sees the request exactly as the client
sent it and keeps its own caching policy. The details, and how to add your own
middleware, are in Configuration.
Plugins
Features live in plugins, each a package in plugins/* or node_modules:
- Separate packages. A plugin compiles to its own
dist/folder. Its imports of React, Hono, Drizzle and other packages stay imports, so the app installs them once. - Declared, not wired by hand. The plugin declares its routes, AdminCP navigation and database tables, and the app's build generates the imports.
- Only configured plugins ship. A plugin that is not listed in
vitnode.config.tsandvitnode.api.config.tsadds no code to the build.