Skip to content

Your First Project

Quick Start showed one file. A real project uses a config file and migrations. This guide builds that scaffold.

  • Directorymy-app/
    • mountsqli.config.ts
    • Directorysrc/
      • db.ts
      • main.ts
    • package.json
src/tables.ts
import { defineTable, int, text, timestamp } from "@mountsqli/core";
export const posts = defineTable("posts", {
id: int().pk(),
title: text().notNull(),
body: text(),
createdAt: timestamp().notNull().defaultNow(),
});

mountsqli.config.ts is the single source of truth. The Db type is derived from it — never hand-rolled.

mountsqli.config.ts
import { defineConfig } from "@mountsqli/core";
import { posts } from "./src/tables";
export default defineConfig({
driver: "sqlite",
url: "./app.db", // file URL persists _mount_migrations
tables: [posts],
});
src/db.ts
import { mountsqli, type DbFromConfig } from "@mountsqli/core";
import config from "../mountsqli.config";
export const db = mountsqli(config);
export type AppDb = DbFromConfig<typeof config>;
Terminal window
npx mountsqli migrate generate
npx mountsqli migrate apply

This creates posts and records the step in _mount_migrations.

src/main.ts
import { db, posts } from "./db";
await db.from(posts).insert({ title: "Hello", body: "First post" });
const all = await db.from(posts).all();
console.log(all);
flowchart TD
  A[tables.ts] --> B[mountsqli.config.ts]
  B --> C[db.ts: typed Db]
  C --> D[migrate generate]
  D --> E[_mount_migrations]
  C --> F[your app queries]
  • Keep tables in their own module and import them into the config.
  • Commit mountsqli.config.ts; it is your schema of record.
  • Use a file URL (not :memory:) so migrations persist between runs.
  • Editing the table in code but forgetting migrate generate — the live DB drifts.
  • Deriving Db by hand instead of DbFromConfig — you lose type accuracy.