Skip to content

Schema

Define database tables using TypeScript and sync them automatically.

Quick Start

typescript
import { Database, model, string, number, boolean } from "kyrin";

// Define a model with id column
const User = model("users", {
  id: number(), // Primary key (auto-increment)
  name: string(),
  email: string().optional(),
  isActive: boolean().default(true),
});

// Create database and sync
const db = new Database({ type: "sqlite", filename: "./app.db" });
db.register(User);
db.sync({ force: true }); // Creates table

Note: The id column must be explicitly defined. When named id, it automatically becomes INTEGER PRIMARY KEY AUTOINCREMENT.

Type Functions

FunctionSQL TypeExample
string()TEXTname: string()
number()INTEGERage: number()
boolean()INTEGER (0/1)active: boolean()
date()TEXT (ISO)createdAt: date()
array()TEXT (JSON)tags: string().array()

Modifiers

Chain modifiers to customize column behavior:

typescript
const Post = model("posts", {
  id: number(), // Primary key (auto-increment)
  title: string(),
  subtitle: string().optional(), // NULL allowed
  views: number().default(0), // Default value
  tags: string().array(), // Stored as JSON
  deletedAt: date().nullable(), // Explicit NULL
});
ModifierEffect
.optional()Removes NOT NULL
.default(value)Adds DEFAULT clause
.nullable()Allows NULL
.array()Stored as JSON TEXT
.pk()Set as PRIMARY KEY

Custom Primary Key

By default, the id column becomes INTEGER PRIMARY KEY AUTOINCREMENT. Use .pk() for custom primary keys:

typescript
const Product = model("products", {
  sku: string().pk(),        // sku is primary key
  name: string(),
  price: number()
});

// Usage - same API, different primary key
const product = Product.create(db, { sku: "ABC123", name: "Laptop" });
const found = Product.findOne(db, { sku: "ABC123" });
Product.update(db, { sku: "ABC123" }, { price: 999 });
Product.delete(db, { sku: "ABC123" });

Syncing

Force Mode (Development)

Drops and recreates tables. ⚠️ Data loss!

typescript
db.sync({ force: true });

Safe Mode (Production)

Creates tables if not exist, adds new columns only.

typescript
db.sync(); // Safe by default

Dry Run

Returns SQL without executing:

typescript
const sql = db.sync({ dryRun: true });
console.log(sql);
// ["CREATE TABLE IF NOT EXISTS users ...", ...]

CRUD Operations

All operations are performed through the model:

Create

typescript
const user = User.create(db, {
  name: "John",
  email: "john@example.com",
});
console.log(user.id); // Auto-generated ID

Find All

typescript
// All records
const users = User.findAll(db);

// With filter
const activeUsers = User.findAll(db, { isActive: true });

Find One

typescript
const user = User.findOne(db, { id: 1 });
if (user) {
  console.log(user.name);
}

Update

typescript
User.update(db, { id: 1 }, { name: "Jane" });

Delete

typescript
User.delete(db, { id: 1 });

Type Inference

Access the inferred TypeScript type:

typescript
const User = model("users", {
  id: number(), // Primary key (auto-increment)
  name: string(),
  email: string().optional(),
});

// Get type from model
type UserType = typeof User.$type;

// UserType = {
//   id: number;
//   name: string;
//   email?: string;
// }

Generated SQL

The id column is automatically set as PRIMARY KEY AUTOINCREMENT:

typescript
const User = model("users", {
  id: number(), // Primary key (auto-increment)
  name: string(),
  isActive: boolean().default(true),
});

User.toCreateSQL();
// CREATE TABLE IF NOT EXISTS users (
//   id INTEGER PRIMARY KEY AUTOINCREMENT,
//   name TEXT NOT NULL,
//   isActive INTEGER NOT NULL DEFAULT 1
// )

Multiple Models

Register multiple models at once:

typescript
const User = model("users", { ... });
const Post = model("posts", { ... });
const Comment = model("comments", { ... });

db.register(User, Post, Comment);
db.sync({ force: true });

Best Practices

  1. Use force: true only in development - It drops all data
  2. Always define an id column - Creates INTEGER PRIMARY KEY AUTOINCREMENT
  3. Use .optional() for nullable fields - Cleaner than .nullable()
  4. Use .default() for sensible defaults - Applied at database level

Released under the MIT License.