Database
Getting Started with D1 Models
This documentation describes how to use Superflare's D1 Models.
Model Attributes
Any properties defined on your model are accessible on the model instance:
import { Model } from "superflare";
export class Post extends Model {
get upperTitle() {
return this.title.toUpperCase();
}
toJSON(): PostRow {
return super.toJSON();
}
}
export interface Post extends PostRow {}
const post = new Post();
post.title = "Hello World";
console.log(post.title); // "Hello World"
console.log(post.upperTitle); // "HELLO WORLD"
Primary Key
The primary key of a model is always id
. You can't change it. Because I said so.
Timestamps
Superflare automatically adds createdAt
and updatedAt
timestamps to all models. These are automatically updated by Superflare.
If you want to disable this behavior, you can set timestamps
to false
in your model:
import { Model } from "superflare";
export class Post extends Model {
static timestamps = false;
}
Serializing Models to JSON
We live in a full-stack framework world, where UI can be written as modular components (like React and Vue) and can be shared and rendered on both the server and the client.
However, we cannot simply send the entire data model over the wire from the server to the client — this would mean huge client bundle sizes and loads of security issues.
Instead, modern frameworks will serialize the output sent (from "loaders" in Remix, or passed as props from server components to client components in Next.js).
Superflare makes it obvious how to serialize your models as JSON by providing the standard toJSON()
method by on new models.
By default, this method will return all of the model's attributes, in addition to any relations that are loaded on the instance either manually or by using eager-loading.
You can override this method to customize the JSON output:
import { Model } from "superflare";
export class Post extends Model {
toJSON(): Pick<PostRow, "title" | "body"> {
return {
title: this.title,
body: this.body,
};
}
}
Be sure to update the return type of the toJSON
method to match the return type of your custom implementation. Front-end frameworks will use this type to determine the shape of the serialized data that is consumed in the actual UI layer.
Hidden Attributes
In some cases, you might want to hide sensitive data from appearing in the serialized versions of your models. For example, you might want to hide a user's password
from appearing in the serialized version of a User
model.
While other ORM tools provide a way to define "hidden" attributes at the model level, Superflare assumes that you will update the toJSON
method to customize the serialized output of your models.
import { Model } from "superflare";
export class User extends Model {
toJSON(): Omit<UserRow, "password"> {
const { password, ...rest } = super.toJSON();
return rest;
}
}
Computed fields
In some cases, you might want to add a field to your model that is not stored in the database. For example, you might want to add a fullName
field to your User
model that is the combination of the firstName
and lastName
fields.
You can do this by defining a getter method on your model:
import { Model } from "superflare";
export class User extends Model {
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
toJSON(): UserRow & { fullName: string } {
return {
...super.toJSON(),
fullName: this.fullName,
};
}
}
Casting Attributes
Superflare does not provide a mechanism for defining custom attribute cast behavior. By default, it will convert any date fields in the database to JavaScript Date
objects, and back to their original shape when toJSON
is called.
If you need to customize the casting behavior for a specific attribute, you can override the toJSON
method:
import { Model } from "superflare";
export class User extends Model {
toJSON(): UserRow {
const json = super.toJSON();
json.createdAt = json.createdAt.toISOString();
return json;
}
}
Querying Models
Find
To find a model by its primary key, use the find
method:
const post = await Post.find(1);
You can also find multiple models by passing an array of primary keys:
const posts = await Post.find([1, 2, 3]);
Where
To find a model by a specific attribute, use the where
method:
const post = await Post.where("title", "Hello World");
Where In
To find a model which matches one of many specific attributes, use the whereIn
method:
const post = await Post.whereIn("title", ["Hello World", "Hello Universe"]);
All
To find all models, use the all
method:
const posts = await Post.all();
First
To find the first model, use the first
method:
const post = await Post.first();
Creating Models
To create a new model, use the create
method:
const post = await Post.create({
title: "Hello World",
body: "This is my first post!",
});
You can also instantiate a new model and then save
it:
const post = new Post({
title: "Hello World",
body: "This is my first post!",
});
await post.save();
Updating Models
To update a model, use the save
method:
const post = await Post.find(1);
post.title = "Hello World";
await post.save();
You can also update multiple model attributes by passing an object to the update
method:
const post = await Post.find(1);
await post.update({
title: "Hello World",
body: "This is my first post!",
});
Deleting Models
To delete a model, use the delete
method:
const post = await Post.find(1);
await post.delete();