An appRouter knows all the routes and their handlers; deploying your backend is as easy as deploying the appRouter. Since JStack is built on top of Hono, you can deploy this router anywhere: Vercel, Netlify, Cloudflare, AWS, Railway, etc.
Creating the appRouter
An appRouter is built on top of a base api that defines global behaviors. For example, where your API is served from, error handling, 404 response handling, or global middleware:
server/index.ts
import { j } from "./jstack"import { postRouter } from "./routers/post-router"// 1️⃣ Creating the base API with global configurationconst api = j .router() .basePath("/api") .use(j.defaults.cors) .onError(j.defaults.errorHandler)// 2️⃣ Merging with feature routersconst appRouter = j.mergeRouters(api, { post: postRouter,})export type AppRouter = typeof appRouterexport default appRouter
Configuration Options
All JStack routers are lightweight, minimal extensions built on top of the Hono API. Any method you can call on the Hono API, you can also call on JStack routers:
I recommend using JStacks default error handler via j.defaults.errorHandler. It catches all router errors and returns standardized error responses that you can easily handle on the frontend.
server/index.ts
import { j } from "../jstack"const api = j .router() .basePath("/api") .use(j.defaults.cors) .onError(j.defaults.errorHandler)
api.onError((err, c) => { console.error(`${err}`) return c.text("Custom Error Message", 500)})
Base Path
To configure where your API is served from, adjust the basePath parameter and rename your api directory to match this new path:
// 👇 Serve all routes under /api/*const api = j .router() .basePath("/api") .use(j.defaults.cors) .onError(j.defaults.errorHandler)
CORS
Nobody likes CORS (🤮), but it's a necessary evil for security reasons. I recommend using JStack's default CORS middleware:
server/index
import { InferRouterInputs, InferRouterOutputs } from "jstack"import { j } from "./jstack"import { postRouter } from "./routers/post-router"const api = j .router() .basePath("/api") .use(j.defaults.cors) .onError(j.defaults.errorHandler)
You can also customize CORS if needed using Hono's built-in CORS middleware. In this case, it's very important to whitelist the x-is-superjson header, as JStack uses this internally for c.superjson() responses:
import { cors } from "hono/cors"cors({ allowHeaders: ["x-is-superjson"], exposeHeaders: ["x-is-superjson"], origin: (origin) => origin, // default: allow any origin credentials: true, // default: allow credentials})
Inferring Router Inputs/Outputs
import type { AppRouter } from "./jstack"import type { InferRouterInputs, InferRouterOutputs } from "jstack"type InferInput = InferRouterInputs<AppRouter>type InferOutput = InferRouterOutputs<AppRouter>// 👇 Usage: InferInput[<router>][<procedure>]type Input = InferInput["post"]["example"]type Output = InferOutput["post"]["example"]