Quickstart
Build a frontend + backend Todo app in 5 minutes
Let's build a Todo app with authentication, CRUD operations, and realtime updates — from scratch in 5 minutes.
1. Create the Project
npx gencow@latest init my-todo --template task-app
cd my-todo && bun install2. Explore the Schema
Open gencow/schema.ts — the template already includes a tasks table:
import { pgTable, serial, text, boolean, timestamp } from "drizzle-orm/pg-core";
import { ownerRls } from "@gencow/core";
import { user } from "./schema-auth";
export const tasks = pgTable("tasks", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
done: boolean("done").default(false).notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
createdAt: timestamp("created_at").defaultNow().notNull(),
}, (t) => ownerRls(t.userId));Security: Gencow injects the signed-in user into
ctx.db, so PostgreSQL Row-Level Security (RLS) policies declared in your schema apply automatically in procedures andcreateCrud()— no manual.where(eq(...))for basic owner isolation.
ownerRls(t.userId)is the common preset: it matches each row's owner column to the auth session. WithoutownerRlsortableRls,ctx.dbqueries can return and mutate all rows in the table. Usectx.dbfor normal app logic; reservectx.unsafeDbfor audited admin paths only.
createCrud()layers auth (401 for unauthenticated callers by default), input validation, and realtime invalidation on top of RLS. Seegencow/SECURITY.md(scaffolded from the template) for the full checklist — multi-tenanttableRls, storage grants, AI proxy rules, and deploy audit.⚠️ Schema rule:
createCrud()requires anidprimary key (serial,text, oruuid). This template already includes one.
3. Define the CRUD API
In gencow/tasks.ts, use createCrud() from ./runtime (not @gencow/core):
import { createCrud } from "./runtime";
import { tasks } from "./schema";
export const tasksCrud = createCrud(tasks, { prefix: "tasks" });createCrud(tasks, { prefix: "tasks" }) generates:
- Read procedures:
tasks.list→{ data, total },tasks.get→ single row by id - Write procedures:
tasks.create,tasks.update,tasks.remove— auth + realtime invalidation by default - Security: Authentication required unless you pass
{ allowAnonymous: true } - Access gates:
access.read/create/update/removerun before database access and can deny the operation - Selective exposure: Use
methods: ["list", "get"](or any subset) to generate only the operations you want to expose - Multiple CRUD views: You can call
createCrud()multiple times for the same table, but each one must use a differentprefix - ID auto-detection:
serial→ number,text/uuid→ string
task-app template: Ships hand-written
procedurehandlers intasks.tsas a reference. You can replace them withcreateCrudas above for less boilerplate.Need custom logic? Add
procedure.query/procedure.mutationalongsidecreateCrud()— see Queries Guide and Mutations Guide.
4. Register API Definitions
Open gencow/index.ts — import CRUD definitions and register them with defineApi:
import "./runtime";
import { defineApi } from "@gencow/core";
import { tasksCrud } from "./tasks";
export default defineApi({
crud: {
tasks: tasksCrud,
},
});The server exposes only what you register here. Feature modules are not auto-discovered, and methods controls which generated procedures end up in api.ts.
5. Start the Dev Server
gencow devOn first run, gencow dev codegens the typed client API, syncs schema-derived migrations, and starts the local API server with hot reload. You should see output like:
Gencow Dev
▸ Backend root: ./gencow
▸ Storage: ./.gencow/uploads
▸ DB: Cloud PostgreSQL
✓ Schema → migrations synced
✓ Generated src/gencow/api.ts
✓ Generated gencow/README.md (AI vibe-coding guide)
Starting server with hot-reload...
✓ Server running at http://localhost:5456
✓ Dashboard: http://localhost:5456/_admin
✓ Schema → migrations synced—gencow devautomatically runsnpx drizzle-kit generateto create SQL migration files ingencow/migrations/.gencow deploy(production) also auto-runs this before bundling — soschema.tschanges are always captured without a manual step.
✓ Generated src/gencow/api.ts— This file is generated from the procedures you registered ingencow/index.ts. Do not edit it manually.
Verify the API
Open the admin dashboard at http://localhost:5456/_admin to:
- Browse your database tables
- View registered procedures
- Test API endpoints
6. Connect a Vite + React Frontend
For a new UI, use Vite + React conventions: src/main.tsx as the entry point, src/App.tsx for the app shell, and import.meta.env.VITE_API_URL for the backend URL. Do not choose Next.js unless the user explicitly asks for Next.js/SSR or you are working inside an existing Next.js project.
Install the React SDK:
npm install @gencow/client @gencow/reactSet Up Auth
// src/lib/auth.ts
import { createAuthClient } from "@gencow/client";
const baseUrl = import.meta.env.VITE_API_URL ?? "http://localhost:5456";
export const auth = createAuthClient(baseUrl);
export const { signIn, signUp, signOut } = auth;
Create the runtime client
// src/lib/apiClient.ts
import { createGencowClient } from "@gencow/client";
import { api } from "../gencow/api";
import { auth } from "./auth";
const baseUrl = import.meta.env.VITE_API_URL ?? "http://localhost:5456";
export const apiClient = createGencowClient({ api, baseUrl, auth });Set Up the Provider
// src/main.tsx
import { GencowProvider } from "@gencow/react";
import { apiClient } from "./lib/apiClient";
function AppWrapper({ children }) {
return (
<GencowProvider apiClient={apiClient}>
{children}
</GencowProvider>
);
}Build the Todo UI
import { useQuery, useMutation } from "@gencow/react";
import { api } from "./gencow/api"; // auto-generated by `gencow dev`
export default function App() {
// useQuery returns { data, isLoading, error, ... }
// createCrud().list returns { data: Task[], total: number }
const { data: result, isLoading, error } = useQuery(api.tasks.list, {});
const { mutate: createTask, isPending: isCreating } = useMutation(api.tasks.create);
const { mutate: updateTask } = useMutation(api.tasks.update);
const { mutate: removeTask } = useMutation(api.tasks.remove);
if (isLoading && !result) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>My Todos ({result?.total ?? 0})</h1>
{/* Create form */}
<form onSubmit={(e) => {
e.preventDefault();
const title = new FormData(e.currentTarget).get("title") as string;
createTask({ title });
e.currentTarget.reset();
}}>
<input name="title" placeholder="New task..." required />
<button disabled={isCreating}>
{isCreating ? "Adding..." : "Add"}
</button>
</form>
{/* Task list — use result.data (not result directly) */}
<ul>
{result?.data.map((t) => (
<li key={t.id}>
<span
onClick={() => updateTask({ id: t.id, done: !t.done })}
style={{ cursor: "pointer" }}
>
{t.done ? "✅" : "⬜"} {t.title}
</span>
<button onClick={() => removeTask({ id: t.id })}>🗑️</button>
</li>
))}
</ul>
</div>
);
}Realtime sync: When you call
createTask(), the server invalidatestasks.listsubscribers over WebSocket. Each connected client refetches with its own auth token — no manualrefetch()needed. Authenticated queries are not pushed with full row payloads (that would leak data); only public/anonymous endpoints may receive direct pushes.
7. Add Seed Data (Optional)
Create gencow/seed.ts to populate the database with test data:
import { tasks } from "./schema";
export default async function seed(ctx) {
// Create a test user first (auth is handled separately)
// Then seed tasks
await ctx.db.insert(tasks).values([
{ title: "Learn Gencow", userId: "test-user-id" },
{ title: "Build an awesome app", userId: "test-user-id" },
{ title: "Deploy to cloud", userId: "test-user-id" },
]);
}# Run seed (server must be running)
gencow db:seed8. Deploy
# Login (first time only)
gencow login
# Start dev (real-time backend deployment)
gencow dev
# Deploy frontend (if you have one)
VITE_API_URL=https://<app-id>.gencow.app npm run build
gencow static dist/That's it! Your app is live on https://<app-id>.gencow.app. 🎉
gencow devwatches for file changes and auto-deploys.gencow staticdeploys your built frontend.For production deployment on Startup and Enterprise plans, use
gencow deploy --prod.
Next Steps
- Project Structure — Understand every config file
- Schema Guide — Advanced schema patterns
- Authentication — Auth setup in detail
- AI Engine — Add AI to your app
- Deployment — Cloud deployment details