Scope a collection (single physical store)

For single-table/collection backends (e.g., PouchDB, Local Storage), scope each collection so queries only return documents for that logical collection.

Quick Navigation

Steps

  1. Add a tracked computed discriminator to your schema that stores the logical collection name (for example, documentType).
  2. Apply a global scope when creating the collection so all queries are constrained to that discriminator.
import { s } from "@routier/core/schema";
import { DataStore } from "@routier/datastore";

// Define a discriminator that tags each record with its logical collection name
export const productsSchema = s
    .define("products", {
        id: s.string().key().identity(),
        name: s.string(),
    })
    .modify((x) => ({
        // Persist the logical collection name for single-table/collection backends
        documentType: x.computed((_, collectionName) => collectionName).tracked(),
    }))
    .compile();

// Apply a global scope so all queries are constrained to this collection
export class AppDataStore extends DataStore {
    products = this.collection(productsSchema)
        .scope(([e, p]) => e.documentType === p.collectionName, { ...productsSchema })
        .create();
}

Runtime Scope + Inferred Types

When the scope value is only known at runtime (for example, current userSub), initialize those collections in the constructor via factory functions.
Use ReturnType<...> on the collection properties to keep the same inferred collection types you would get from direct property initialization.

import { DataStore } from "@routier/datastore";
import { userSchema, userOrganizationSchema } from "./schemas";

export class AppDataStore extends DataStore {
    constructor(private readonly userSub: string) {
        super();

        this.users = this.usersFactory(userSub);
        this.userOrganizations = this.userOrganizationsFactory(userSub);
    }

    usersFactory = (userSub: string) =>
        this.collection(userSchema)
            .scope(([x, p]) => x.userRef === p.userSub, { userSub })
            .create();

    userOrganizationsFactory = (userSub: string) =>
        this.collection(userOrganizationSchema)
            .scope(([x, p]) => x.userRef === p.userSub, { userSub })
            .create();

    // Keep strong inferred collection types from the factory return types.
    users: ReturnType<AppDataStore["usersFactory"]>;
    userOrganizations: ReturnType<AppDataStore["userOrganizationsFactory"]>;
}

Why

This prevents cross-type collisions when multiple entity types share a single physical table/collection.


This site uses Just the Docs, a documentation theme for Jekyll.