Skip to content

Typed ids

Often, databases have similarly sounding entities, e.g., user and account. To prevent developers from mixing those and introducing stealthy bugs, Typesaurus makes all ids typed.

By default, a collection id is linked to its path.

import { schema } from "typesaurus";
const db = schema(($) => ({
users: $.collection<User>(),
accounts: $.collection<Account>().sub({
reports: $.collection<Report>(),
}),
}));

In the given example, the user document id is Id<"users">, and the account’s reports subcollection document id is Id<"users/reports">.

Mixing types or trying to use a string will cause TypeScript to complain about it:

db.users.update(accountId, { name: "Sasha" });
//=> Argument of type 'Id<"accounts">' is not assignable to parameter of type 'Id<"users">'.

Creating id

If you need to convert a string to an id or create a new random id, you can use a collection method id:

// Convert string to an id:
const userId = db.users.id("sasha");
// Create a new random id:
const newUserId = await db.users.id();

Read more about the id method


To access a subcollection id, use the sub property:

const id = await db.users.sub.notes.id();

Read more about accessing subcollections

Id type

If you need to use an id type in a function or another type, you can utilize the inferred schema type:

import { schema, Typesaurus } from "typesaurus";
export const db = schema(($) => ({
// ...
}));
export type Schema = Typesaurus.Schema<typeof db>;
function removeAccount(accountId: Schema["accounts"]["Id"]) {
return db.accounts.remove(accountId);
}
function publishReport(
accountId: Schema["accounts"]["Id"],
reportId: Schema["accounts"]["sub"]["reports"]["Id"],
) {
return db.accounts(accountId).reports.update(reportId, { published: true });
}

Read more about inferring schema

Shared ids

If your collections share ids, i.e., you use the organization id to store subscription documents, you can define a custom id using Typesaurus.Id:

import { schema, Typesaurus } from "typesaurus";
const db = schema(($) => ({
organizations: $.collection<Organization>(),
subscriptions: $.collection<Subscription, Typesaurus.Id<"organizations">>(),
}));

Now, you’ll be able to use the organization id to operate on the subscription documents:

function removeOrganization(organizationId: Schema["organizations"]["Id"]) {
return Promise.all([
db.organizations.remove(organizationId),
db.subscriptions.remove(organizationId),
]);
}

Read more about the Id type

When to use shared ids

Static ids

Some collections have a finite number of documents, i.e., global application stats. For those cases, you can define custom string ids:

import { schema } from "typesaurus";
const db = schema(($) => ({
regionStats: $.collection<RegionStats, "us" | "europe" | "asia">();
}));
db.regionStats.update("us", ($) => {
online: $.increment(1);
});

When to use static ids