Type-Safe File-Based REST API Router

Define schemas once.
Let types cascade everywhere.

File-based routing for TypeScript REST APIs. Compose scoped directory middleware with full type inference, enforce compile-time return contracts, and eliminate type assertions forever.

import { json } from '@taserjs/router/reply'import { z } from 'zod'import { t } from '@taserjs/router'const GET = t.get('/dashboard/users')  .query(z.object({    page: z.coerce.number().default(1),    limit: z.coerce.number().default(10),  }))export type RouteContext = typeof GET.$Infer.Contextexport default GET.handler(async (ctx) => {  const sub = ctx.state.jwtPayload.sub  const { page, limit } = ctx.query  const users = await ctx.db.getUsers(page, limit)  return json({ sub, users })})
E2E Type Safe

The Problem & Solution

Why traditional Node.js routing breaks at scale

Traditional routers force trade-offs between clean folder structures and real type safety. Taser eliminates the type assertion trap and guarantees runtime correctness from middleware to client.

Traditional API (Express / Fastify)
Type Casting Required
server.ts
// Middleware attaches data to reqapp.use((req, res, next) => {  req.user = getUser(req); // untyped assignment  next();});// Downstream handler has NO type inferenceapp.post("/private", validateBody(schema), (req, res) => {  const user = req.user as User;       // ⚠️ manual typecast  const body = req.body as PostBody;   // ⚠️ manual typecast  const query = req.query as Query;    // ⚠️ manual typecast  res.send({ ok: true });});
Taser Router
100% Inferred Context
routes/admin.ts + routes/admin/reports.post.ts
// Middleware validates and passes state to next()export default t.layout("/admin/*").use(async (ctx, next) => {  const token = ctx.headers.get("authorization");  const user = await verifyUser(token);  if (!user) throw new Error("Unauthorized");  return next({ user }); // Merges into ctx.state});// Handler receives fully-inferred ctx automaticallyconst POST = t.post("/admin/reports").body(ReportInputSchema);export default POST.handler(async (ctx) => {  const user = ctx.state.user; // ✓ Inferred User from next({ user })  const body = ctx.body;       // ✓ Inferred ReportInput  return json({ created: true, by: user.id });});
Key Takeaway: In Taser, middleware return state flows directly into ctx.state with zero typecasting or Express global interface hacks.

Mental Model

Inspired by TanStack Router. Engineered for REST APIs.

TanStack Router revolutionized client routing with file-system layouts and type inference. Taser brings that same intuition to backend HTTP servers: HTTP verb files for handlers, non-verb files for cascading directory middleware.

Taser Router
Backend REST API
File StructureTypeMatched Endpoint
$.ts
MW
/*
index.get.ts
GET
/
posts.ts
MW
/posts/*
posts
/posts
index.get.ts
GET
/posts
index.post.ts
POST
/posts
$postId.get.ts
GET
/posts/$postId
_auth.ts
MW
/posts/*
_auth
/posts
$postId.put.ts
PUT
/posts/$postId
$postId.delete.ts
DELETE
/posts/$postId

How the mental model translates to the backend

In Taser Router, files are treated differently depending on their suffix:

HTTP Verb Files(Handlers)

Endpoints are segregated by method suffixes: .get.ts, .post.ts, .put.ts, .delete.ts, .patch.ts, and all standard HTTP verbs.

Non-Verb Files(Middleware)

Files without a verb (e.g. $.ts, posts.ts, _auth.ts) act as scoped middleware. They run before child handlers and cascade typed context down the folder tree.

Quick Start

Scaffold your stack in seconds

Interactive CLI with batteries included: pick your framework, Nitro preset, database ORM, Standard Schema validator, and logger.

bash — create-taserjs
create-taserjsv0.1.0
Project name
my-api
Host framework
None (Standalone)Hono / Fetch-NativeExpressFastify
Deployment target (preset)
Node ServerNode ClusterStandalone ViteBunDeno ServerDeno DeployCloudflareVercelAWS LambdaNetlify
Database
DrizzlePrismaKyselyNone
Database driver
PostgreSQLSQLiteMySQL
Logger
PinoWinstonNone
Validator
ZodArkTypeValibot
Scaffolded ./my-api successfully!
Created None (Standalone) app targeted for Node Server with Drizzle (SQLite), Zod validation, and Pino logging.

Configure Your Stack

One-Liner Command
pnpm create taserjs@latest my-api --framework none --preset node-server --db drizzle:sqlite --logger pino --validator zod -y

Run this command to bypass interactive prompts and generate your project instantly in CI or your terminal.

Roadmap

What the future looks like

We're actively building the next generation of type-safe backend tooling. Here is what is on our horizon.

In Development

OpenAPI Specification Generation

Auto-generate OpenAPI 3.1 YAML and JSON endpoints directly from your file routes, Standard Schema definitions, and return contracts. Zero manual Swagger spec writing.

OpenAPI 3.1 YAML/JSONSwagger UI endpointZero schema duplication
In Development

ESLint Plugin for Router Rules

Dedicated @taserjs/eslint-plugin to enforce HTTP verb suffixes (.get.ts, .post.ts), catch invalid param patterns, and validate route exports right in your editor.

File naming linterExport contract checksCLI & CI/CD guards
Planned

WebSockets & Server-Sent Events

Realtime streaming routes (.ws.ts, .sse.ts) with typed event schemas, connection lifecycle hooks, and bi-directional type safety for live updates.

.ws.ts & .sse.ts file routesTyped event channelsStreaming client SDK
Planned

Distributed Rate Limiting & Observability

Distributed Redis sliding-window rate limiting, OpenTelemetry distributed tracing spans, Prometheus metrics exporters, and automated ETag cache middleware.

Distributed Rate LimitingOpenTelemetry TracesETag & Cache Controls

Open Source & Independent

Backed by the community. Built for everyone.

Taser is 100% open source. Sponsoring funds continuous performance optimizations, runtime adapter development, codegen tooling, and long-term maintenance.

Sponsor Taser Development

Support sustainable open source tooling. Contributions directly support new runtime adapters, compiler features, instant watch-mode codegen, and Standard Schema integrations.

Get Started

Ready to ship type-safe REST APIs?

Explore the documentation, pick your framework adapter, and build handlers with zero manual route registries and zero type casting.

Read the Documentation
Kazi Ahmed

Kazi Ahmed

Creator

Crafting type-safe developer tools, runtime adapters, and deterministic routing engines for TypeScript.