MantisDocs

Queries

Select, insert, update and delete with a client typed from your schema.

The client

lib/mantis.ts
import { createClient } from "@shipmantis/sdk";
import schema from "../mantis/schema";

export const mantis = createClient({
  url: process.env.MANTIS_URL!,
  key: process.env.MANTIS_SECRET_KEY!,
  schema,
});
OptionWhat it does
urlhttps://data.shipmantis.com, written to .env.local by init.
keyThe secret key on a server, the publishable key in a browser.
schemaYour schema. It is only used for types, so tables, columns and rows are checked.
branchThe branch to query. Default: main.
userTokenA function that returns the signed-in user's token, for access rules.

mantis.from(table) gives you the table's client. Every call returns the rows it read or changed, typed from the schema.

Select

const todos = mantis.from("todos");

const open = await todos.select({
  columns: ["id", "title"],
  where: { done: false, dueAt: { lt: new Date().toISOString() } },
  orderBy: [{ column: "dueAt", direction: "asc" }],
  limit: 20,
  offset: 40,
});
//    ^? { id: string; title: string }[]
  • columns picks the columns to return. Leave it out for all of them.
  • orderBy sorts by one or more columns, asc by default. Rows are always sorted by id last, so pages are stable. Without orderBy, rows come oldest first.
  • limit is 100 by default and at most 1000. offset skips rows.

Where

Every key in where must hold, so the conditions are joined with AND. A plain value means equal; an object compares:

OperatorMeans
eq, neequal, not equal
lt, lte, gt, gteless than, at most, greater than, at least
inequal to one of a list
await todos.select({ where: { listId: { in: [inbox, today] }, title: { ne: "" } } });

null asks for an empty column: { dueAt: null } and { dueAt: { eq: null } } both mean is null, and { dueAt: { ne: null } } means is not null. A ne with a value keeps the rows where the column is empty.

Insert

const [todo] = await todos.insert({ title: "Ship it", listId: inbox });
await todos.insert([{ title: "One", listId: inbox }, { title: "Two", listId: inbox }]);

Pass one row or an array of up to 1000. Columns that are optional or have a default can be left out. The rows come back with id and createdAt filled.

Update and delete

await todos.update({ set: { done: true }, where: { id: todo.id } });
await todos.delete({ where: { done: true } });

Both need a where, so a missing filter can't change every row by accident. To mean every row, say so with where: {}.

Errors

A refused call throws a MantisError with the HTTP status and a list of reasons:

import { MantisError } from "@shipmantis/sdk";

try {
  await todos.insert({ title: "No list" } as never);
} catch (error) {
  if (error instanceof MantisError) {
    console.log(error.status, error.reasons);
    // 422 [ ... ]
  }
}
StatusWhen
401The user token is not valid for the project, or the schema has no auth to check it.
402The team's plan is out of room. Delete rows or move to Pro.
403The access rules refuse the call.
422The query is not valid, or Postgres refused a value or a constraint. Nothing was changed.

Every write is recorded, so anything a query does can be rewound.

On this page