Serverless-first Deno framework

Build type-safe APIs at the speed of thought

Thunder is a serverless-first, full-stack framework for Deno with file-based routing, Zod-validated schemas, a powerful hooks system, and instant CRUD - plus a generated admin dashboard, so you ship product, not boilerplate.

12k+validated req/s
1 cmdto add auth + RBAC
0 linesof frontend for a dashboard
terminal
git clone https://github.com/Huruf-Tech/thunder.git my-appcd my-app && deno task initdeno task dev

Ship in hours, not weeks

Thunder collapses the work between idea and production API. The plugin system, instant CRUD, and zero-config routing do the heavy lifting for you.

1 command

Plugins instead of plumbing

Every Thunder project is a plugin. Install thunder-core to get auth, RBAC, multi-tenancy, OAuth2, API keys and a double-entry wallet - in a single command.

1 function

Instant CRUD

createCRUD generates create, read, update, delete, and count endpoints for any model - fully validated, with filtering, sorting and pagination built in.

0 config

File-based routing

Drop a file in routes/ and it becomes an endpoint. No registries, no wiring - convention over configuration from the first line.

Free

OpenAPI, SDK + dashboard

Your routes already describe their types. Thunder turns them into an OpenAPI spec, a typed client SDK, and a complete admin dashboard.

Less boilerplate. More shipping.

Define a validated route, generate a full CRUD API, or intercept requests with hooks - all with end-to-end type safety.

routes/users.ts

A fully validated route

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.string().email(),    });    const $return = z.object({ id: z.string() });    return {      shape: () => ({ body: $body, return: $return }),      handler: async (req) => {        const body = $body.parse(await bodyAsJson(req));        return Response.created({ id: "123" });      },    };  });}).group("Users");
routes/todos.ts

A complete CRUD API in one call

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("/api", function todos(router) {  createCRUD({    router,    schema: $todo,    model: todoModel,    insertSchema: $todoInput,  });}).group("Todos");
hooks/auth.ts

Intercept requests with hooks

import type { THook } from "@/core/http/hooks.ts";import { Response } from "@/core/http/response.ts";export default {  priority: 100, // higher runs first  pre: async ({ req }) => {    const token = req.headers.get("Authorization");    if (!token) return Response.unauthorized();  },} satisfies THook;
Thunder UI

Your backend already knows what the UI looks like

Thunder UI is the framework's built-in dashboard. One command generates a React app from your schemas - navigation, list pages, filters, create and edit forms, and reference pickers - then stays a normal React app you can build anything in.

deno task generate:app
schemas/employee.ts

You write this on the backend

export const employeeInput = z.object({  fname: z.string().max(50).meta({    group: "name",    groupClassName: "grid grid-cols-2 gap-4",    label: "First Name",  }),  lname: z.string().max(50).meta({    group: "name",    label: "Last Name",  }),  department: $objectId.meta({    ref: "departments",    refLabel: "title",    canFilter: true,  }),  salary: z.number().meta({ fieldHint: "amount" }),  status: z.enum(Status).meta({ canFilter: true }),});
/acme/hr/employees/form

You get this, with no frontend code

Driven by your schema

Zod .meta() decides grouping, labels, controls and which columns are filterable.

Permission-aware

Your thunder-core roles are the navigation - a module hides itself when a role can't see it.

Bilingual with RTL

English and Arabic ship translated, with full right-to-left support and bundled fonts.

Override any screen

Swap a generated table for your own card view, or replace a page outright - the rest keeps working.

Explore Thunder UI

Security patches land with one re-sync - your own code is never touched.

Hybrid mobile

The same dashboard, shipped to Android and iOS

Thunder UI is a Capacitor 8 app as well as a web app. Its native layout is one adaptive shell - a real app on a phone, a sidebar on a desktop - so going mobile is a build and a sync, not a second codebase to maintain.

Feels like an app

A floating tab bar, large-to-compact titles, and an edge swipe back with 1:1 tracking and momentum.

Right on the first frame

Phone or desktop is decided by CSS, not a measured width - plus safe-area insets and status bar colours.

Push notifications

Device tokens register through @capacitor/push-notifications and post straight to your backend.

Fonts work offline

Latin and Arabic webfonts are bundled, not fetched - so text renders with no connection.

terminal
VITE_APP_LAYOUT=nativenpx cap syncnpx cap open android   # or: ios
Read the mobile guide
Benchmarks

Fast where it counts

A validated POST /users endpoint under load (50 connections, no database). Thunder leads on the Deno runtime and outpaces popular Node frameworks like Express - while doing full Zod validation on every request.

POST /usersrequests / second · higher is better
ThunderDeno
12,384
OakDeno
11,091
ExpressNode
8,148

Everything you need, nothing you don't