Skip to content
Tikab's Toolkit

Create your own project

The Toolkit is the part you keep; the example app is the part you replace. This guide walks through standing up your own product on the stack.

What you take, what you write

Loading diagram...

Step by step

Scaffold a TanStack Start app

Create a fresh TanStack Start project (Vite-based) with Bun as the runtime. Raw source exports require a TypeScript-capable bundler — Vite handles this out of the box.

Install the Toolkit and its peers
bun add @tikab-interactive/toolkit
bun add drizzle-orm pg zod better-auth @tanstack/react-table lucide-react

The Toolkit declares infrastructure clients as regular dependencies and the shared-by-host libraries (React, Drizzle, the router) as peers, so your app controls the versions and nothing is bundled twice.

Compose your database schema

The foundation fragment (auth tables, mailbox, audit log, runtime config) comes from the Toolkit. Your domain tables live in your repo, referencing the foundation — never the other way around:

// db/schema/index.ts — your composition point
export * from "@tikab-interactive/toolkit/db/foundation";
export * from "./app"; // your domain: customers, orders, whatever you build
// db/schema/app.ts — your fragment
import { pgTable, integer, text, timestamp } from "drizzle-orm/pg-core";
import { user } from "@tikab-interactive/toolkit/db/foundation";
 
export const order = pgTable("order", {
  id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
  createdById: text("created_by_id").references(() => user.id),
  createdAt: timestamp("created_at").notNull().defaultNow(),
});

Point drizzle.config.ts at your composition file and generate your first migration — it will cover the foundation and your tables in one 0000_*.sql:

bun run drizzle-kit generate
bun run drizzle-kit migrate
Mount auth and the file proxy

Two thin routes wire the Toolkit into your app — copy them from the example as reference implementations:

  • src/routes/api/auth/$.ts — hands /api/auth/* to Better Auth.
  • src/routes/api.files.ts — serves stored objects through getObject (pairs with fileUrl() from the storage block).

Set the required environment: DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL. Password-reset emails already flow through the mailer — they land in the outbox until you configure SMTP_*.

Stand up the infrastructure

Copy example/docker-compose.yml as your starting point: Postgres, MinIO (with the bucket-creating init job), optionally Hatchet for jobs and pgweb (--profile tools) as the database browser. Every service is self-hosted — nothing phones home.

Copy the reference surfaces you want

These are deliberately app code, not packages — they carry your branding, your i18n, your routes. Take them from the example and make them yours:

  • Login / signup / forgot-password / reset-password routes (the auth UI building blocks live in the ui package; the routes are thin).
  • The /admin area — compose AdminShell, DataTable, SelectionBar, EditSheet from the admin-ui block against your server functions (see The admin area for the shape).
  • The /sandbox harness skeleton + its guard (open in dev, site-admin-only in production).
Adopt the working rules
  • Sandbox-first for every new building block.
  • The gates: typecheck, lint (oxlint), fmt (oxfmt), and two e2e tiers (journey + building-block specs).

What stays yours

Everything with domain knowledge: your schema fragment, your server functions, your routes and UI, your permission rules, your job tasks, your seed script. The Toolkit never imports any of it — the dependency arrow always points from your app into the Toolkit.

Checklist

  • Schema composed (foundation + yours), migration 0000 generated and applied
  • Auth route mounted; sign-up → sign-in → sign-out works
  • File proxy route serving uploads from the storage block
  • .env.example in your repo documenting required vs optional variables
  • compose file with Postgres + MinIO (+ Hatchet/pgweb as needed)
  • /admin composed and gated on isSiteAdmin
  • Sandbox + the two e2e tiers in place