Typebase saca la autorización de tu backend de las políticas RLS de Postgres y la mueve a middleware de TypeScript que el compilador sí revisa. El schema, las actions y la autenticación viven como ficheros .ts en una carpeta typebase/ dentro de tu app actual —sin repo aparte, sin dashboard— y el frontend las llama como si fueran funciones locales, tipadas de extremo a extremo.
La landing lo resume en una línea: “Write actions, a database schema, and auth as TypeScript files in a typebase/ folder inside your app. Your frontend calls them like local functions, end-to-end typed, zero REST boilerplate.” Apareció como Show HN el 26 de agosto de 2026 y juntó unas decenas de votos y un hilo de comentarios que vale la pena leer; volvemos a eso al final.
¿Qué es Typebase?
Es un framework de backend que vive dentro de tu proyecto de frontend. Agregas una carpeta typebase/, escribes ahí tu schema de Postgres, tus server actions y tu configuración de auth como ficheros TypeScript, y un CLI genera un cliente tipado y despliega el servidor.
La estructura, tal como aparece en la guía de inicio:
typebase/
├── _generated/ (auto-generated, do not edit)
├── db/
│ ├── schema.ts
│ └── relations.ts
├── actions/
│ ├── queries/
│ │ └── todos.ts
│ └── mutations/
│ └── todos.ts
├── typebase.json
└── env.ts
Debajo no hay un stack nuevo: el schema es Drizzle ORM, la autenticación es un envoltorio fino sobre better-auth, la validación de entrada acepta cualquier validador compatible con Standard Schema (Zod, Valibot, ArkType) y la base de datos es Postgres estándar. El realtime va por Server-Sent Events. Los frontends soportados son Next.js, SvelteKit, Nuxt y Expo.
¿Qué es RLS en Supabase y por qué Typebase quiere reemplazarlo?
RLS —Row Level Security— es el mecanismo de Postgres que decide, fila por fila, quién puede leer o escribir qué. En Supabase es el lugar donde vive buena parte de la lógica de autorización: escribes políticas en SQL y la base de datos las aplica en cada consulta.
El argumento de Typebase es que ese SQL queda fuera del alcance de tus herramientas. Su página de comparación dice de Supabase que “most logic lives in the database as SQL or RLS policies” y que Supabase “doesn’t have a strong concept of typed server functions”. Sobre los tipos generados sostiene que “with Supabase, types are generated from your database schema as a separate step. They can drift out of sync”.
La versión más filosa del reclamo es sobre los agentes de código: con Supabase, “much of the business logic lives in SQL and Row Level Security policies, where the agent can’t lean on the TypeScript compiler at all”.
Conviene ser claro sobre qué es esto: es el encuadre que hace un proveedor de su competidor, no una evaluación independiente. Pero la observación de fondo es una que mucha gente ya se comió. Una política RLS es una cadena de SQL que tu editor no typechequea, que tu suite de tests casi nunca ejercita y sobre la que tu agente no puede razonar como razona sobre una función. La respuesta de Typebase es convertir la autorización en una función.
¿Cómo se instala Typebase?
Dos comandos para agregarlo, uno para armar el andamiaje:
npm install typebase-io
npm install -D typebase-io-cli
npx typebase-io-cli init
init acepta flags —--with-auth, --with-db-publisher y --skip-example— así que puedes generar el fichero de auth y el publisher de eventos desde el arranque en lugar de agregarlos después.
Después vas a usar estos tres comandos todo el tiempo:
npx typebase-io-cli codegen # regenera el cliente tipado
npx typebase-io-cli db dev push # aplica cambios de schema a la base
npx typebase-io-cli deploy dev # despliega el servidor
¿Cómo se define el schema?
En typebase/db/schema.ts, al estilo Drizzle, a través del export p de Typebase:
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(),
});
Luego npx typebase-io-cli db dev push lo aplica. Fíjate en la palabra push: las migraciones propiamente dichas están en el roadmap, no entregadas. Al momento de publicar esta nota, el roadmap lista “allow users to use migrations” como pendiente. Si vas a correr algo con datos reales adentro, ese es el punto a vigilar.
¿Cómo se escribe una action?
Una action es una cadena: .input(), .output(), .handler(). Queries y mutations son la misma primitiva —la documentación es explícita: “there’s no technical difference between a ‘query’ and a ‘mutation’ in Typebase. They’re both actions, and the distinction is purely organizational.”
import { ServerError } from 'typebase-io/server';
import { z } from 'zod';
import { action } from '../../_generated/server.ts';
export const getOne = action
.input(z.object({ id: z.number() }))
.output(z.object({ id: z.number(), value: z.string(), completed: z.boolean() }))
.handler(async ({ db, input }) => {
const todo = await db.query.todos.findFirst({
where: { id: input.id },
});
if (!todo) throw new ServerError('NOT_FOUND');
return { id: todo.id, value: todo.value, completed: todo.completed };
});
El contexto del handler se arma según lo que exista en tu carpeta: db (cliente Drizzle tipado) si existe db/schema.ts, auth si existe auth.ts, env si configuraste variables de entorno, publisher si tienes un publisher, y reqHeaders siempre. Los errores son ServerError con un nombre de código de estado —NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST y otros—, cada uno mapeado a un status HTTP.
También existe .stream() en lugar de .handler() cuando quieres una respuesta por SSE.
¿Cómo funciona la autorización sin RLS?
Con middleware. .use() recibe el contexto y devuelve valores que se fusionan en el contexto del handler, completamente tipados. El patrón de action protegida que trae la documentación es este:
export const authedAction = action.use(async ({ reqHeaders, auth }) => {
if (!reqHeaders) {
throw new ServerError('UNAUTHORIZED');
}
const sessionData = await auth.api.getSession({
headers: reqHeaders,
});
if (!sessionData?.session || !sessionData?.user) {
throw new ServerError('UNAUTHORIZED');
}
return {
user: sessionData.user,
};
});
Ahí está todo el argumento, en diez líneas. authedAction es un valor sobre el que construyes las demás actions, user llega al handler tipado, y si renombras un campo del objeto de usuario el compilador te dice cada lugar que se rompe. El equivalente en RLS es una expresión de política dentro de la base que ninguna parte de tu toolchain lee.
La autenticación en sí es un solo fichero, typebase/auth.ts:
import { defineAuth } from 'typebase-io/server';
export const auth = defineAuth({
trustedOrigins: ['http://localhost:3000'],
emailAndPassword: {
enabled: true,
},
});
La documentación describe a defineAuth como “a thin wrapper around better-auth”: acepta las opciones de better-auth excepto database, que Typebase administra. Así que socialProviders y el resto de la superficie de better-auth están disponibles acá.
¿Cómo llama el frontend a las actions en Next.js?
Creas un cliente a partir del tipo Router generado. Versión simple:
import { createRouterClient } from 'typebase-io/client';
import type { Router } from '../../typebase/_generated/server';
export const client = createRouterClient<Router>({
url: process.env.TYPEBASE_APP_URL_DEV || process.env.TYPEBASE_APP_URL || '',
});
O la versión con TanStack Query, si quieres caché y hooks en client components:
import { createTanstackQueryClient } from 'typebase-io/client';
import type { Router } from '../../typebase/_generated/server';
export const client = createTanstackQueryClient<Router>({
url: process.env.NEXT_PUBLIC_TYPEBASE_APP_URL || '',
});
Y después, en Next.js:
// Server Component
const todos = await client.queries.todos.getMany();
// Server Action
await client.mutations.todos.create({ value: value.trim() });
// Client Component, con TanStack Query
const { data: todos } = useQuery(client.queries.todos.getMany.queryOptions());
La autenticación del lado del cliente sale del mismo paquete:
import { createAuthClient } from 'typebase-io/client/auth/react';
export const authClient = createAuthClient();
authClient.useSession() en client components; getServerSession desde typebase-io/client/auth/nextjs en el servidor.
Las variables de entorno que crea deploy son TYPEBASE_APP_URL_DEV para desarrollo y TYPEBASE_APP_URL para producción. El cliente de TanStack necesita una legible desde el navegador: NEXT_PUBLIC_TYPEBASE_APP_URL.
¿Es gratis y dónde se despliega?
El repositorio typebase-io/monorepo es público y está bajo licencia MIT, y los paquetes se instalan desde npm. Al momento de publicar esta nota, ni el sitio ni la documentación publican una página de precios.
El despliegue va a Vercel, Cloudflare Workers o Deno Deploy, con Postgres en Neon. También puedes generar el código del servidor y hospedarlo por tu cuenta. Es decir: la infraestructura es tuya y la pagas donde ya la pagas, que es exactamente el punto que Typebase marca contra Supabase y Convex —“Supabase and Convex host your backend on their infrastructure. Migrating away is a significant amount of work.”
¿Qué le falta todavía?
La documentación es inusualmente directa sobre esto, lo cual es buena señal. Al momento de publicar esta nota:
- Storage y mailers no existen. Ambos figuran como “coming soon (BYO via any TS library)” en la página de comparación, y “add storage support” / “add mailer support” aparecen como pendientes en el roadmap.
- Las migraciones tampoco están. El camino actual es
db dev push. - El realtime es más manual que en Convex. Palabras del propio proyecto: “that’s more to write than Convex, and less automatic than subscribing to a table.”
- No hay broadcast channels ni presence. La comparación dice sin vueltas que “Typebase has no equivalent for” esas funciones de Supabase.
Contra Convex el argumento es la portabilidad: Convex “uses a proprietary database. Your data is tied to their platform, and you can’t use the SQL knowledge, tooling, or ecosystem you already have.” Typebase corre sobre Postgres estándar que es tuyo.
¿Typebase o Supabase?
Si estás arrancando una app con Next.js, SvelteKit, Nuxt o Expo y tu reflejo es Supabase, esta es la alternativa que cambia una cosa concreta: dónde vive la autorización. Todo lo demás —Drizzle, better-auth, Zod, Postgres— son piezas que probablemente ibas a elegir igual. Lo que agrega Typebase es el cableado y el deploy.
Si hoy necesitas almacenamiento de ficheros, email transaccional, presence o una historia de migraciones, todavía no está listo para ti.
Una última cosa, porque dice algo del proyecto. Los testimonios de la landing son falsos, y el sitio lo admite. El hilo de Show HN se partió al medio: un comentarista escribió “I love the (well-disclosed) fake testimonials. Hilarious!” y otro que “fake reviews are a hard no-no imo.” El comentario más útil del hilo fue probablemente el más plano: “It’s like Next.js but for backend… But the wheel needs to be reinvented on how to do everything.” Ese es el trade-off real. Ganas una única forma coherente y tipada de hacer las cosas, y resignas todas las recetas que ya te sabías.
Si te interesa esta categoría, hace poco cubrimos InstantDB 1.0, que ataca el mismo problema desde el ángulo opuesto.