---
title: API reference
description: Every database method, query modifier and filter operator in the Zeitlos SDK.
sidebarLabel: API reference
---

The database surface of `@zeitlosapp/sdk`.

> [!IMPORTANT]
> These calls are **server-side only** — server components, route handlers, server actions.
> See the [quickstart](/database/quickstart/).

## Create Client

To operate with the database you'll need to create a client first.

```ts
import { createClient } from '@zeitlosapp/sdk';

const client = createClient(); // zero-config in a hosted app
const client = createClient<Database>(); // Define types for the shape of your database
```

## Methods

```ts
await client.from('posts').list();
await client.from('posts').get(id);
await client.from('posts').create({ title: 'Hello' });
await client.from('posts').update(id, { published: true });
await client.from('posts').delete(id);
```

| Method              | Returns                            | Notes                                       |
| ------------------- | ---------------------------------- | ------------------------------------------- |
| `list()`            | `{ records, nextCursor?, count? }` | Applies any chained modifiers               |
| `get(id)`           | the record                         | Throws `record_not_found` if missing        |
| `create(input)`     | the created record                 | Don't pass `id`, `created_at`, `updated_at` |
| `update(id, input)` | the updated record                 | Only the fields you pass                    |
| `delete(id)`        | `void`                             |                                             |

## Query modifiers

Chain these before `list()`:

| Modifier             | Description                                                  |
| -------------------- | ------------------------------------------------------------ |
| `.filter(filter)`    | Restrict results — see below                                 |
| `.sort(...fields)`   | `'field'` ascending, `'-field'` descending; multiple allowed |
| `.select(...fields)` | Return only these fields                                     |
| `.expand(...fields)` | Inline related records one level deep, for `relation` fields |
| `.limit(n)`          | Page size — the gateway caps it                              |
| `.cursor(c)`         | Fetch the page after a previous result's `nextCursor`        |
| `.withCount()`       | Also return the total match `count`                          |

```ts
const { records, nextCursor, count } = await client
  .from('posts')
  .filter({ published: true })
  .sort('-created_at')
  .limit(20)
  .withCount()
  .list();

const nextPage = await client.from('posts').cursor(nextCursor).list();
```

## Filters

A filter is an object. A bare value means equals; an object means `{ operator: value }`.
Top-level fields are ANDed together.

```ts
.filter({ status: 'open' })                      // equals
.filter({ views: { gte: 100 } })                 // one operator
.filter({ title: { contains: 'sale' } })         // string operator
.filter({ tag: { in: ['a', 'b'] } })             // membership
.filter({ archivedAt: { isNull: true } })        // null check
```

Compose with `and`, `or` and `not`:

```ts
.filter({ or: [{ pinned: true }, { featured: true }] })
.filter({ and: [{ views: { gte: 100 } }, { status: 'open' }] })
.filter({ not: { status: 'draft' } })
```

### Operators

| Operator                  | Meaning                 |
| ------------------------- | ----------------------- |
| `eq` / `neq`              | Equals / does not equal |
| `gt` / `gte`              | Greater than / or equal |
| `lt` / `lte`              | Less than / or equal    |
| `in` / `nin`              | In / not in a list      |
| `contains`                | String contains         |
| `startsWith` / `endsWith` | String prefix / suffix  |
| `isNull`                  | Is (or is not) null     |

Which operators are valid depends on the field's type. The gateway rejects an illegal
combination with a `ZeitlosError`.

## Errors

Every failure throws a `ZeitlosError` with a machine-readable `.code` and, for gateway
errors, an HTTP `.status`.

```ts
import { ZeitlosError } from '@zeitlosapp/sdk';

try {
  await client.from('posts').get(id);
} catch (err) {
  if (err instanceof ZeitlosError && err.code === 'record_not_found') {
    // 404
  }
}
```

| Code                   | When                                                    |
| ---------------------- | ------------------------------------------------------- |
| `record_not_found`     | `get`, `update` or `delete` on an id that doesn't exist |
| `validation_failed`    | A value didn't match the field's type or constraints    |
| `unique_violation`     | A unique field already holds that value                 |
| `forbidden`            | An access rule or the key's tier denied it              |
| `invalid_filter`       | A filter used an operator the field doesn't support     |
| `table_not_found`      | No such table in this project                           |
| `field_not_found`      | No such field on that table                             |
| `database_not_enabled` | The project has no database yet                         |
| `quota_exceeded`       | A plan limit was hit                                    |
| `network_error`        | The service couldn't be reached                         |
| `config_error`         | No URL or key available to the client                   |

Treat unknown codes generically — the list isn't closed.

## SDK limitations

The SDK currently does not support any APIs to manage the schema of your database. Creating or altering tables and fields happens in the dashboard or through the
[MCP server](/getting-started/mcp-server/).

There is no SQL API available.
