Typebase: je backend is een map met TypeScript

Voorbeeld van implementatie

Server-side: typebase/actions/queries/todos.ts

import { action } from '../../_generated/server';
import { z } from 'zod';

export const getMany = action
  .output(z.array(z.object({
    id: z.number(),
    value: z.string(),
  })))
  .handler(async ({ db }) => {
    return db.query.todos.findMany();
  });

Client-side: src/app/page.tsx

import { client } from '@/lib/typebase/client';

export default async function Page() {
  const todos = await client.queries.todos.getMany();
  // ^? { id: number; value: string }[]

  return todos.map((t) => (
    <li key={t.id}>{t.value}</li>
  ));
}

Typebase biedt eersteklas gidsen voor:

  • Next.js
  • SvelteKit
  • Nuxt
  • Expo

Voor elk framework zijn er idiomatische clients beschikbaar: Server Components, SvelteKit load-functies, Nuxt-plugins en Expo SecureStore.

Gebouwd op: Drizzle ORM, RPC en better-auth.

---

Hoe het werkt

Van nul naar een geïmplementeerde backend in twee commando's. Alles gebeurt binnen je eigen codebase; de CLI regelt de codegeneratie, schema-pushes en implementatie.

1. Scaffold

Eén commando maakt een typebase/ map aan in je bestaande app, inclusief een databaseschema, voorbeeldacties en optionele authenticatie of realtime publishing. Geen aparte repository, geen dashboard. $ npx typebase-io-cli init

2. TypeScript schrijven

Definieer tabellen in db/schema.ts, exporteer acties vanuit actions/, en voeg auth.ts, env.ts en publisher.ts toe voor authenticatie, omgevingsvariabelen en realtime functionaliteit. Elke export wordt end-to-end typechecked.

3. Implementeren

Je map wordt als server verzonden naar Vercel, Cloudflare Workers of Deno Deploy, met Postgres op Neon. Je kunt ook de servercode genereren en deze overal hosten. Typebase beheert zelf geen servers. $ npx typebase-io-cli deploy

---

Gedetailleerd overzicht van de mappenstructuur

typebase/

  • actions/
  • mutations/
  • todos.ts
  • queries/
  • todos.ts
  • db/
  • relations.ts
  • schema.ts
  • auth.ts
  • env.ts
  • publisher.ts

Bestanden en configuratie

typebase/db/schema.ts (Jouw definities) Gebruik Drizzle onder de motorkap.

import { p } from 'typebase-io/db';

export const todos = p.pgTable('todos', {
  id: p.integer().primaryKey().generatedAlwaysAsIdentity(),
  value: p.varchar({ length: 255 }).notNull(),
  completed: p.boolean().notNull(),
  createdAt: p.timestamp().notNull().defaultNow(),
});

typebase/db/relations.ts (Jouw definities) Registreert tabellen voor db.query.todos.*

import { q } from 'typebase-io/db';
import * as schema from './schema.ts';

export const relations = q.defineRelations(schema, (r) => ({
  todos: {},
}));

typebase/actions/queries/todos.ts (Jouw definities) Wordt client.queries.todos.getMany()

import { z } from 'zod';
import { action } from '../../_generated/server.ts';

export const getMany = action
  .output(z.array(z.object({
    id: z.number(),
    value: z.string(),
    completed: z.boolean(),
  })))
  .handler(async ({ db }) => {
    return db.query.todos.findMany();
  });

typebase/actions/mutations/todos.ts (Jouw definities) Wordt client.mutations.todos.create()

import { z } from 'zod';
import { action } from '../../_generated/server.ts';
import { todos } from '../../db/schema.ts';

export const create = action
  .input(z.object({ value: z.string().min(1) }))
  .handler(async ({ db, input }) => {
    await db.insert(todos).values({
      value: input.value,
      completed: false,
    });
  });

typebase/auth.ts (Jouw definities) Sessies, OAuth, e-mail/wachtwoord in één bestand.

import { defineAuth } from 'typebase-io/server';

export const auth = defineAuth({
  trustedOrigins: ['http://localhost:3000'],
  emailAndPassword: { enabled: true },
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  },
});

typebase/env.ts (Jouw definities) Als er iets mist, start de server niet op.

import { defineEnv } from 'typebase-io/server';
import { z } from 'zod';

export const env = defineEnv({
  RESEND_API_KEY: z.string().min(1),
  STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
});
// Gebruik in elke actie: ({ env }) => env.RESEND_API_KEY

typebase/publisher.ts (Jouw definities) Events die je acties publiceren en streamen.

import { definePublisher } from 'typebase-io/server';
import { z } from 'zod';

export const publisher = definePublisher({
  provider: 'db',
  events: {
    'todo.created': z.object({ id: z.number() }),
  },
});
// Gebruik vervolgens: .stream(async function* ({ publisher }) { ... })

---

Het probleem met RLS

Row Level Security (RLS) is impliciet en geschreven in een SQL-dialect dat je editor niet typecheckt. Eén UPDATE-policy geeft schrijftoegang tot elke kolom, inclusief de kolommen die je morgen toevoegt. Een te permissive clause die een AI-agent om 02:00 uur 's nachts heeft toegevoegd, glipt door de review, omdat geen enkele compiler dit zal signaleren.

Met Typebase is autorisatie expliciet. Je actie verklaart welke kolommen hij accepteert en je auth-check wordt in code uitgevoerd voordat er iets de database bereikt. Als je een kolom toevoegt, vertelt de compiler je wie er in mag schrijven. Dezelfde code die je agent schrijft, is de code die je compiler controleert.

---

Vergelijking: De DX van Convex, de openheid van Supabase

Typebase is ontstaan vanuit de behoefte aan backend-functies die in je eigen code leven, ondersteund door een database die je daadwerkelijk bezit.

KenmerkTypebaseSupabaseConvex
Backend logicaTypeScript functiesSQL + RLS policiesTypeScript functies
DatabaseStandaard PostgresPostgresProprietary
Type safetyEnd-to-end, altijd synchroonGegenereerd, kan afwijkenEnd-to-end
RealtimeStreaming acties over SSERealtime subscriptionsReactive queries
Authbetter-auth, in één bestandIngebouwd, dashboard configThird-party providers
InfrastructuurJouw cloud: Vercel, Cloudflare, DenoSupabase-hostedConvex-hosted
Vendor lock-inGeen, altijd uitstapmogelijkheidMediumHigh

Let op: Storage is nog niet beschikbaar; dit staat als volgende op de roadmap.

---

Getuigenissen

Let op: Geen van deze mensen bestaat. We hebben het twee keer gecontroleerd. Legal vindt het prima.

"Ik heb afgelopen kwartaal 40.000 regels REST-plumbing verwijderd. Mijn tech lead begon te huilen. Ik denk dat het gelukstranen waren. Ik heb opgehouden met vragen."
Mariel Vonnegut, Principal Eng, AI unicorn
"Voor Typebase had ik drie acroniemen in mijn pipeline: REST, gRPC en WHY. Nu heb ik er één: fn(). Ik ben nog nooit gelukkiger geweest en mijn Oura-ring is het ermee eens."
Dave Dave, Senior Fullstack, waarschijnlijk
"Ik heb vier verschillende therapeuten verteld over Typebase. Twee stopten me als cliënt. De andere twee bouwen nu een app ermee."
Clementine Ryu, Engineer, tussen therapeuten door
"Mijn co-founder vroeg waar de auth staat. Ik zei: 'in een bestand genaamd auth.ts'. Hij heeft sindsdien niet meer tegen me gesproken. Ik neem aan dat hij onder de indruk is."
Tomás Lindberg, Indie hacker, mogelijk single
"We hebben 14 microservices vervangen door één map. Het DevOps-team gaf een feestje voor me. Het feestje was een vergadering. De vergadering ging over ontslagen."
Anoniem, om voor de hand liggende redenen
"10/10 zou mijn backend opnieuw een map maken."
Hannah Pollard, Senior Folder Engineer, zelfbenoemd

---

Geef je AI-agent een backend die hij kan lezen

Het kost ongeveer negentig seconden. Het grootste deel daarvan is npm install.

$ npm i typebase-io && npm i -D typebase-io-cli