Basic D1 Backed Model
In this section, we will explore the basic properties of a D1 backed Model in Cloesce. Cloudflare D1 is a serverless SQL database built on SQLite for Workers.
Defining a Model
Note
Models do not have constructors as they should not be manually instantiated. Instead, use the ORM functions to create, retrieve, and update Model instances. For tests, consider using
Object.assign()to create instances of Models with specific property values.
Tip
Using the
@PrimaryKeydecorator is optional if your primary key property is namedidor<className>Id(in any casing, i.e., snake case, camel case, etc). Cloesce will automatically treat a property namedidas the primary key.
Compilation in Cloesce consists of three phases: Extraction, Analysis, and Code Generation.
During Extraction, Cloesce scans your source files (designated with *.cloesce.ts) for Model definitions. Models are defined using the @Model() decorator.
import { Model, Integer, PrimaryKey } from "cloesce/backend";
@Model("db")
export class User {
@PrimaryKey
id: Integer;
name: string;
}
The above code defines a Model “User” stored in the D1 database db, with several properties:
| Property | Description |
|---|---|
User | Cloesce infers from the class attributes that this Model is backed by a D1 table User |
id | Integer property decorated with @PrimaryKey, indicating it is the Model’s primary key. |
name | String property representing the user’s name; stored as a regular column in the D1 database. |
Supported D1 Column Types
Cloesce supports a variety of column types for D1 Models. These are the supported TypeScript types and their corresponding SQLite types:
| TypeScript Type | SQLite Type | Notes |
|---|---|---|
Integer | INTEGER | Represents an integer value |
string | TEXT | Represents a string value |
boolean | INTEGER | 0 for false, 1 for true |
Date | TEXT | Stored in ISO 8601 format |
number | REAL | Represents a floating-point number |
Uint8Array | BLOB | Represents binary data |
All of these types by themselves are NOT NULL by default. To make a property nullable, you can use a union with null, e.g., property: string | null;. undefined is reserved for Navigation Properties and cannot be used to indicate nullability.
Notably, an Integer primary key is automatically set to AUTOINCREMENT in D1, so you don’t need to manually assign values to it when creating new records (useful for the ORM functions).
Fluent API
Some column configurations cannot be cleanly expressed through TypeScript decorators alone. For these cases, Cloesce provides a Fluent API that can called in cloesce.config.ts to further customize the D1 schema. For example, to make a column unique:
import { defineConfig } from "cloesce/config";
import { Weather } from "./src/data/models.cloesce";
const config = defineConfig({
// ...
});
config.model(Weather, builder => {
builder.unique("dateTime", "location");
});
Additionally, Cloesce exposes a method to modify the AST after extraction:
config.rawAst((ast) => {
// modify the raw AST here
});
Migrating the Database
Important
Any change in a D1 backed Model definition (adding, removing, or modifying properties; renaming Models) requires a new migration to be created.
The migration command will generate a new migration file in the
migrations/directory.
The standard Cloesce compilation command does not perform database migrations. To create or update the D1 database schema based on your Model definitions, you need to run the migration command:
npx cloesce compile # load the latest Model definitions
npx cloesce migrate <d1-binding> <migration name>
Finally, these generated migrations must be applied to the actual D1 database using the Wrangler CLI:
npx wrangler d1 migrations apply <d1-binding>