Introduction

Why Thunder - a serverless-first Deno framework that lets you ship type-safe APIs in hours, not weeks.

Thunder is a serverless-first, full-stack framework for Deno. It pairs file-based routing, Zod-validated schemas, a powerful hooks system, and instant CRUD with a plugin ecosystem - so you spend your time shipping product, not wiring infrastructure.

If you have ever wanted the type-safety of a modern stack without the configuration tax that usually comes with it, Thunder is built for you.

Why Thunder

Ship in hours, not weeks

Instant CRUD, zero-config routing, and ready-made plugins collapse the distance between an idea and a production API.

Type-safe end to end

Zod validates params, query, body, and response while TypeScript enforces your return shape at compile time.

Serverless-first

Stateless, isolated requests with fast cold starts and predictable memory - designed for serverless from day one.

Batteries via plugins

Every Thunder project is a plugin. Install thunder-core for auth, RBAC, OAuth2, and security in one command.

Faster development, by design

Thunder removes the repetitive parts of building an API so your team moves faster:

You want to...With Thunder
Add authentication, RBAC, and security headersdeno task add:plugin -n Huruf-Tech/thunder-core
Build a full CRUD resourceOne createCRUD(...) call
Add an endpointDrop a file in routes/ - no registration
Validate requests and responsesZod schemas the framework enforces for you
Give clients a typed SDKdeno task generate:sdk from your routes

The plugin system is the heart of Thunder's velocity: reuse entire feature sets (auth, multi-tenancy, wallet, OAuth2) by installing a single package, then build on top with hooks.

A validated route, in full

routes/users.ts
import { Router } from "@/core/http/router.ts";
import { bodyAsJson } from "@/core/http/utils.ts";
import z from "zod";

export default new Router("/api", function users(router) {
  router.post("/users", function createUser() {
    const $body = z.object({
      name: z.string().min(1),
      email: z.email(),
    });
    const $return = z.object({ id: z.string() });

    return {
      shape: () => ({ body: $body, return: $return }),
      handler: async (req) => {
        const body = $body.parse(await bodyAsJson(req));
        // ...persist the user
        return Response.created({ id: "123" });
      },
    };
  });
}).group("Users");

A complete CRUD API in one call

routes/todos.ts
import { Router } from "@/core/http/router.ts";
import { createCRUD } from "@/core/utils/createCRUD.ts";
import { todoModel, $todo, $todoInput } from "@/schemas/todo.ts";

export default new Router("/", function todos(router) {
  createCRUD({
    router,
    schema: $todo,
    model: todoModel,
    insertSchema: $todoInput,
  });
}).group("Todos");

Performance

Thunder is fast where it matters. On a validated POST /users endpoint under load (50 connections, no database), it leads on the Deno runtime and outpaces popular Node frameworks - while running full Zod validation on every request.

FrameworkRuntimeReq/sec
ThunderDeno12,384
OakDeno11,091
ExpressNode8,148

These are representative numbers for the validated POST /users workload. See the full benchmarks for methodology, hardware, and results across every endpoint and runtime.

Remember: in a real API, latency is dominated by I/O (database, network). Framework overhead is small - which is why Thunder optimizes for developer experience without giving up speed.

How Thunder is organized

A Thunder project follows a predictable, convention-over-configuration layout: routes/ for endpoints, hooks/ for interceptors, schemas/ for Zod models, and plugins/ for installed feature sets. The core/ directory holds framework internals you never touch.

Ready to build?

Get a Thunder project running locally in a few minutes.

On this page