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.
git clone https://github.com/Huruf-Tech/thunder.git my-appcd my-app && deno task initdeno task devShip 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.
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");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");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;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.
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 }),});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.
Security patches land with one re-sync - your own code is never touched.
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.
VITE_APP_LAYOUT=nativenpx cap syncnpx cap open android # or: iosFast 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.
Everything you need, nothing you don't
File-based Routing
Drop a file in routes/ and it becomes an endpoint. Convention over configuration, with zero wiring.
End-to-end Type Safety
Zod validates params, query, body, and response - and TypeScript enforces your return shape at compile time.
Serverless-first
Stateless requests, fast cold starts, and predictable performance designed for serverless from day one.
Powerful Hooks
Pre and post interceptors with priorities for auth, logging, and rate limiting - no tangled middleware.
Instant CRUD
Generate create, read, update, delete, and count endpoints for any model with a single createCRUD call.
Filtering & Pagination
Typed filters, natural-language dates, sorting and projection on every generated endpoint - reusable in your own routes.
Generated Dashboard
Thunder UI builds an entire React admin app from your schemas. One command, no frontend code.
Multi-tenancy & RBAC
Tenants, members, temporary memberships and permission-gated routes, all from thunder-core.
Plugin Ecosystem
Any Thunder project is a plugin. Install thunder-core for auth, RBAC, and security in one command.