Documentation

Introduction

Declarations ship with the package (index.d.ts, and one per binding), so there is nothing to install.

import sorla from 'sorla-db';
import type { Collection, Document, Query, LiveQuery, BulkWriteOperation, ValidationIssue } from 'sorla-db';

const app = new sorla();
app.useDb('shop');
app.db.createCollection('products');
const products: Collection = app.db.products;

Typing your documents

Collections hold documents of any shape; describe yours with an interface and use it where you read:

interface Product extends Document {
  _id: string;
  name: string;
  price: number;
  tags?: string[];
}

const cheap = products.find({ price: { $lt: 10 } }).toArray() as Product[];
const tea = products.findOne({ _id: 'tea' }) as Product | null;

const live = products.watch<Product>({ available: true });
live.subscribe(list => list.forEach(product => product.price));   // readonly Readonly<Product>[]

The framework bindings are generic too:

const products = useQuery<Product>(app.db.products, { available: true });    // React
const products = useQuery<Product>(app.db.products, { available: true });    // Vue: Ref<readonly Product[]>
const products = querySignal<Product>(app.db.products, { available: true }); // Angular: Signal<readonly Product[]>

Main types

Type What it is
Document Record<string, unknown> & { _id?: unknown }
Query a find query: fields with values or ComparisonOperators, and $and / $or / $nor
UpdateFilter { $set?, $unset?, $inc?, $push?, $pull? }
Projection Record<string, 0 | 1>
SortSpec, SortOptions { field: 1 | -1 }, { nulls?, collation? }
CollectionOptions { nulls?: 'first' | 'last' }
UpdateResult, DeleteResult { matchedCount, modifiedCount, upsertedId }, { deletedCount }
BulkWriteOperation, BulkWriteResult, BulkWriteError see bulkWrite
ChangeEvent, ChangeType event payloads, 'insert' | 'update' | 'replace' | 'delete'
JSONSchema, SchemaFunction, ValidationIssue, ValidationError see Schema validation
BindFormOptions, FormController, FormResult see Forms
LiveQuery<T>, LiveQueryOptions see Live queries
StreamOptions, StreamController, StreamState see Server-Sent Events
ConnectOptions, Remote, LoadParams, LoadResult see WebSocket
Cursor, Collection, Database the classes

Catching validation errors

try {
  products.insertOne({ name: '' });
} catch (error) {
  if (error instanceof sorla.ValidationError) {
    const issues: ValidationIssue[] = error.errors;
  }
}

A typed helper

If you like stricter types at the call site, a small wrapper does it:

function typed<T extends Document>(collection: Collection) {
  return {
    find: (query: Query = {}) => collection.find(query).toArray() as T[],
    findOne: (query: Query) => collection.findOne(query) as T | null,
    insert: (doc: Omit<T, '_id'> & { _id?: T['_id'] }) => collection.insertOne(doc as Document) as T['_id'],
    watch: (query: Query = {}) => collection.watch<T>(query)
  };
}

const productsDb = typed<Product>(app.db.products);
productsDb.insert({ name: 'Coffee', price: 12.5 });