Skip to content

Records

SemitraRecord is the D1-backed model layer.

It keeps persistence, queries, associations, lifecycle hooks, and attachment behavior inside one runtime-aware abstraction.

import { s } from "@semitra/cli";
import ApplicationRecord from "./application_record.ts";
export default class Post extends ApplicationRecord {
static table = "posts";
static schema = s.object({
title: s.string().min(3),
content: s.string().optional()
});
static {
this.hasOneAttached("cover");
}
}

The example app sets these application defaults:

import { SemitraRecord } from "@semitra/cli";
export default class ApplicationRecord extends SemitraRecord {
static database = "DB";
static tenancy = "tenant";
}
  • schema-backed attributes
  • CRUD helpers
  • query chaining
  • associations
  • validations
  • lifecycle hooks
  • attachment helpers
  • tenant-aware context

Common record entry points include:

  • all()
  • find(id)
  • findBy(criteria)
  • where(criteria)
  • whereIn(attribute, values)
  • orderBy({ createdAt: "desc" })
  • limit(count)
  • includes("association")

Semitra prefers structured ordering over raw SQL string order clauses.

A typical request-time record flow looks like this:

const Posts = this.model(Post);
const recentPosts = await Posts.all()
.orderBy({ createdAt: "desc" })
.limit(10);
const post = Posts.build({
title: input.post.title,
content: input.post.content
});
if (await post.save()) {
await post.attachment("cover").attach(coverBytes, {
filename: "cover.png",
contentType: "image/png"
});
}

That keeps D1 persistence, validation, and attachment ownership in the record layer while still using storage for the binary body.

Records support lifecycle hooks such as:

  • beforeValidate
  • afterValidate
  • beforeSave
  • afterSave
  • beforeCreate
  • afterCreate
  • beforeUpdate
  • afterUpdate
  • beforeDestroy
  • afterDestroy
  • afterCommit

Jobs can also be dispatched from record events when side effects belong to the state transition itself.

Attachments are part of the record layer but backed by storage adapters.

Semitra supports:

  • named attachments
  • metadata persistence
  • tenant-aware storage prefixes
  • object access through SemitraStorage

This keeps upload semantics close to the record that owns the file, while still leaving storage concerns in the storage subsystem.