Back to Blog
·6 min read·VentureKit Team

File-Based Routing for Serverless APIs: How VentureKit Maps Files to Lambda Endpoints

Next.js popularized file-based routing for frontends. VentureKit brings the same pattern to serverless APIs — drop a TypeScript file, get an API endpoint backed by AWS Lambda.

routingserverlesslambdatypescript

File-Based Routing: A Proven Pattern

Next.js proved that file-based routing eliminates boilerplate. Instead of manually configuring routes, you create files in a directory structure, and the framework discovers them automatically.

VentureKit brings this pattern to serverless API development. Your directory structure is your API:

src/routes/
  ├── health/
  │   └── get.ts           → GET /health
  ├── projects/
  │   ├── get.ts           → GET /projects
  │   ├── post.ts          → POST /projects
  │   └── [id]/
  │       ├── get.ts        → GET /projects/:id
  │       ├── put.ts        → PUT /projects/:id
  │       └── delete.ts     → DELETE /projects/:id
  └── webhooks/
      └── stripe/
          └── post.ts      → POST /webhooks/stripe

No router configuration. No route registration. No middleware chains to set up. Drop a file, get an endpoint.

How It Works

Each route file exports a handler using VentureKit's unified handler pattern. Zod validation is built into the handler config — you pass your schema as body, query, or params and VentureKit validates automatically before your handler runs:

typescript
// src/routes/projects/post.ts
import { handler } from '@venturekit/runtime';
import { z } from 'zod';

const CreateProject = z.object({
  name: z.string().min(1),
  description: z.string().optional(),
});

export const main = handler(
  async (body, ctx, log) => {
    const project = await ctx.db.insert('projects', body);
    return project;
  },
  {
    scopes: ['projects.write'],
    body: CreateProject,
  },
);

The handler receives the already-validated body as its first argument. If validation fails, VentureKit returns a structured 422 error with field-level details — your handler never runs.

VentureKit's runtime handles everything around your business logic:

  • Request parsing — body, query params, path params, headers
  • Authentication — JWT validation and scope checking (when scopes is specified)
  • Validation — Zod schemas on body, query, and params validated before the handler runs
  • Error handling — consistent error format with proper HTTP status codes
  • Logging — structured JSON logs with request context
  • Status codes — auto-detected from HTTP method (201 for POST, 200 for GET, 204 for DELETE)
  • Public vs. Authenticated Endpoints

    The handler's second argument controls access:

    typescript
    // Public endpoint — no auth required
    export const main = handler(fn);
    
    // Authenticated — requires valid JWT with api.read scope
    export const main = handler(fn, { scopes: ['api.read'] });
    
    // Admin only — requires admin scope
    export const main = handler(fn, { scopes: ['admin'] });

    No auth middleware to configure. No route-level guards to maintain. The pattern is declarative and type-safe.

    Dynamic Parameters

    Square brackets in directory names create dynamic path parameters:

    src/routes/projects/[id]/get.ts     → GET /projects/:id
    src/routes/orgs/[orgId]/users/get.ts → GET /orgs/:orgId/users

    Parameters are available on ctx.params with full TypeScript inference.

    Local Development

    During vk dev, VentureKit scans your routes directory and creates an Express-compatible HTTP server that mimics Lambda behavior:

  • Hot reload when you change a route file
  • Same request/response format as API Gateway
  • Local Postgres, Redis, and S3-compatible storage via Docker Compose
  • Structured logs in your terminal
  • When you deploy with vk deploy, each route becomes an individual Lambda function behind API Gateway — no code changes required.

    Why File-Based Routing for APIs?

    Discoverability

    New developers can understand your entire API surface by looking at the file tree. No need to trace through router configurations or middleware chains.

    Colocation

    Related code lives together. A route's handler, validation schema, and tests all live in the same directory.

    Automatic OpenAPI

    VentureKit generates an OpenAPI specification from your route structure, handler types, and Zod schemas. Your API documentation stays in sync with your code automatically.

    Zero Configuration

    No app.get('/projects/:id', ...) registrations. No Express router imports. No middleware ordering bugs. The file system is the single source of truth.