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
const User = model("users", {
  // id: number(), 
  // currently kyrin handle auto define id attribute name 'id' 
  // on create table but we have plan to define primary key function for optional use
  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

Type Functions

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

Modifiers

Chain modifiers to customize column behavior:

typescript
const Post = model("posts", {
  // id: number(),
  // currently kyrin handle auto define id attribute name 'id' 
  // on create table but we have plan to define primary key function for optional use
  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

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(),
  // id: number(), currently kyrin handle auto define id attribute name 'id' 
  // on create table but we have plan to define primary key function for optional use
  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(),
  // currently kyrin handle auto define id attribute name 'id' 
  // on create table but we have plan to define primary key function for optional use
  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 have an id column - Auto-generates primary key
  3. Use .optional() for nullable fields - Cleaner than .nullable()
  4. Use .default() for sensible defaults - Reduces null checks

Released under the MIT License.