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.