Skip to main content
Manage persistent state across tasks and task runs using mongo-like collections operations. Often your application will have a notion of some sort of stored state, collections are where this state can be stored, then updated, searched and filtered from any task at any time. Collections are built on postgres, and each compose app has it’s own fully isolated postgres database which stores collections data as well as other internal compose data for your app, so you can store even the most important state in a tried and true database.

Examples

import { TaskContext } from "compose";

type Dog = {
  breed: string;
  color: string;
};

export async function main({ collection, fetch }: TaskContext) {
  const dogsCollection = await collection<Dog>("dogs", {
    indexes: [
      { path: "color", type: "text" },
    ],
  });

  // Get existing state
  const brownDogs = await dogsCollection.findMany({ color: "brown" });

  // Get some data
  const newData = await fetch<Dog>("https://api.dogs.com/v1/dogs/labrador");

  // Update state
  await dogsCollection.insertOne(newData);

  return {
    success: true,
  };
}

Full Interface

export type ScalarIndexType = "text" | "numeric" | "boolean" | "timestamptz";

export interface CollectionIndexSpec {
  path: string;
  type: ScalarIndexType;
  unique?: boolean;
}

export interface FindOptions {
  limit?: number;
  offset?: number;
}

// Filter helpers for comparison operators
export type FilterHelper =
  | "$gt"
  | "$gte"
  | "$lt"
  | "$lte"
  | "$in"
  | "$ne"
  | "$nin"
  | "$exists";
export type HelperValue = Partial<
  Record<FilterHelper, string | number | boolean | string[] | number[]>
>;
export type FilterValue = string | number | boolean | HelperValue;
export type Filter = Record<string, FilterValue>;

export type WithId<T> = T & { id: string };

export interface Collection<TDoc = unknown> {
  readonly name: string;
  insertOne(doc: TDoc, opts?: { id?: string }): Promise<{ id: string }>;
  findOne(filter: Filter): Promise<WithId<TDoc> | null>;
  findMany(filter: Filter, options?: FindOptions): Promise<Array<WithId<TDoc>>>;
  getById(id: string): Promise<WithId<TDoc> | null>;
  /**
   * @param opts.upsert - Defaults to true. Set to false to throw if document doesn't exist.
   */
  setById(
    id: string,
    doc: TDoc,
    opts?: { upsert?: boolean },
  ): Promise<{ id: string; upserted?: boolean; matched?: number }>;
  deleteById(id: string): Promise<{ deletedCount: number }>;
  drop(): Promise<void>;
}
The collection function is available on TaskContext:
collection: <T>(
  name: string,
  indexes?: CollectionIndexSpec[],
) => Promise<Collection<T>>;

Next Steps