Show HN: Typebase – A single-folder back end you write in TypeScript
Typebase: Transforming a TypeScript Folder into Your Backend
Imagine your entire backend residing within a single directory. With Typebase, you define your database schema, server-side actions, and authentication logic directly inside a typebase/ folder within your project.
A single command transforms this folder into a fully typed server, allowing your frontend to invoke backend logic as if they were local functions.
🚀 Rapid Integration
Getting started is a breeze. Here is a glimpse of the workflow:
Backend Definition (typebase/actions/queries/todos.ts):
// server-side
import { action } from '../../_generated/server';
import { z } from 'zod';
export const getMany = action.findMany();
Frontend Consumption (src/app/page.tsx):
// client-side
import { client } from '@/lib/typebase/client';
export default async function Page() {
const todos = await client.queries.todos.getMany();
return (
<ul>
{todos.map((t) => (
<li key={t.id}>{t.value}</li>
))}
</ul>
);
}
Typebase provides first-class support and idiomatic clients for:
- Next.js (Server Components)
- SvelteKit (Load functions)
- Nuxt (Plugins)
- Expo (SecureStore)
🛠️ The Architecture
Typebase is engineered using a powerful stack: Drizzle ORM + oRPC + better-auth.
The Deployment Lifecycle
Step-by-Step Workflow
- 01. Scaffold: Run
npx typebase-io-cli initto generate thetypebase/directory, including schema templates and optional auth/realtime setups. - 02. Develop: Write your logic in TypeScript.
- 03. Ship: Run
npx typebase-io-cli deployto push your server to the cloud.
📂 Deep Dive: The typebase/ Directory
Here is how you structure your backend logic:
| File | Purpose | Example Logic |
|---|---|---|
db/schema.ts | Table definitions | export const todos = p.table(...) |
db/relations.ts | DB Query relations | export const relations = q.defineRelations(...) |
actions/queries/ | Read operations | action.findMany() client.queries... |
actions/mutations/ | Write operations | action.create() client.mutations... |
auth.ts | Identity & Access | defineAuth({ emailAndPassword: { enabled: true } }) |
env.ts | Environment Validation | defineEnv({ API_KEY: z.string() }) |
publisher.ts | Real-time Events | definePublisher({ events: { 'todo.created': z.number() } }) |
Implementation Examples
Schema & Relations
// typebase/db/schema.ts
import { p } from 'typebase-io/db';
export const todos = p.table('todos', {
value: p.text(),
completed: p.boolean().default(false),
});
// typebase/db/relations.ts
import { q } from 'typebase-io/db';
import * as schema from './schema.ts';
export const relations = q.defineRelations(schema, (r) => ({
todos: {},
}));
Actions (Queries & Mutations)
// typebase/actions/queries/todos.ts
import { action } from '../../_generated/server.ts';
export const getMany = action.findMany();
// typebase/actions/mutations/todos.ts
import { action } from '../../_generated/server.ts';
import { todos } from '../../db/schema.ts';
export const create = action.create(async ({ input, db }) => {
return await db.insert(todos).values({ value: input.value, completed: false });
});
Auth, Env, and Real-time
// typebase/auth.ts
import { defineAuth } from 'typebase-io/server';
export const auth = defineAuth({
trustedOrigins: ['http://localhost:3000'],
emailAndPassword: { enabled: true },
socialProviders: { github: { clientId: process.env.GITHUB_ID! } },
});
// typebase/env.ts
import { defineEnv } from 'typebase-io/server';
import { z } from 'zod';
export const env = defineEnv({ RESEND_API_KEY: z.string().startsWith('sk_') });
// typebase/publisher.ts
import { definePublisher } from 'typebase-io/server';
import { z } from 'zod';
export const publisher = definePublisher({
provider: 'db',
events: { 'todo.created': z.number() },
});
⚠️ The Problem with RLS (Row Level Security)
Traditional RLS is often invisible implicit. It exists in a SQL dialect that your IDE cannot type-check.
"One single
UPDATEpolicy can accidentally grant write access to every column in a table, including new ones you add months later. If an AI agent slips an overly permissive clause into your SQL at 2 AM, it will pass code review because no compiler is checking it."
The Typebase Approach: Your actions explicitly declare which columns they accept. Authentication checks are executed in code before the data ever touches the database.
Example: An insecure policy like
auth.role() = 'authenticated' allows any signed-in user to modify any row.
📊 Competitive Landscape
Typebase was created to bridge the gap between owning your database and having seamless backend functions.
| Feature | Typebase | Supabase | Convex |
|---|---|---|---|
| Backend Logic | TS Functions | SQL + RLS | TS Functions |
| Database | Standard Postgres | Postgres | Proprietary |
| Type Safety | End-to-end (Synced) | Generated (Can drift) | End-to-end |
| Realtime | SSE Streaming | Subscriptions | Reactive Queries |
| Auth | better-auth (File-based) | Dashboard Config | Third-party |
| Infrastructure | Your Cloud (Vercel/etc) | Supabase Cloud | Convex Cloud |
| Lock-in | None (Eject anytime) | Medium | High |
| Storage | Coming Soon | Available | Available |
🗣️ "Industry-Shaking" Testimonials
*Disclaimer: None of these individuals actually exist.
- Mariel Vonnegut (Principal Eng, AI Unicorn): "Before Typebase, my pipeline was just three acronyms: REST, gRPC, and WHY."
- Dave Dave (Senior Fullstack, maybe): "I've discussed Typebase with four different therapists."
- Clementine Ryu (Engineer, between therapists): "My co-founder asked where the auth lived. I said 'a file called
auth.ts.' He hasn't spoken to me since." - Tomás Lindberg (Indie hacker, possibly single): "We successfully replaced 14 microservices with one folder."
- Anonymous: "10/10 would make my backend a folder again."
- Hannah Pollard (Senior Folder Engineer, self-appointed): "Simply revolutionary."