---
title: Quickstart
description: Enable the database, create a table, and read and write records from your app.
sidebarLabel: Quickstart
---

The Zeitlos database offers a managed data API — tables and records, no SQL. Your app talks to it through the `@zeitlosapp/sdk` package.

> [!NOTE]
> The database is reached through the SDK, and the SDK is TypeScript and JavaScript only for
> now. There is no official client for other languages yet.

## Enable the database

:::steps

1. **Open your project** in the dashboard and go to **Database → Tables**.

2. **Choose "Enable database"**

   A new project has no database until you add one.

3. **Create a table**

   Add a table and its fields using our [MCP server](/getting-started/mcp-server/) with your AI assistent.

:::

> [!NOTE]
> Schemas are not defined in code. There are no migration files in your repository. Editing schemas 
> is currently only possible using the MCP server. We'll
> add schema editing to the dashboard in the future.

## Install the SDK

```bash title="Terminal"
npm install @zeitlosapp/sdk
```

## Create a client

In a Zeitlos-hosted app this is zero-config. The data URL and key are injected into your
app's environment, and the SDK reads them:

```ts title="lib/db.ts"
import { createClient } from '@zeitlosapp/sdk';

export const client = createClient();
```

> [!IMPORTANT]
> The database is **server-side only** for now. Call `client.from(...)` from server components,
> route handlers or server actions — never from the browser. The injected key is a service
> key with full access, and there is no browser-safe flow yet.

## Read and write

```tsx title="app/posts/page.tsx"
import { client } from '@/lib/db';

export const dynamic = 'force-dynamic';

export default async function Posts() {
  const { records } = await client
    .from('posts')
    .filter({ published: true })
    .sort('-created_at')
    .limit(20)
    .list();

  return (
    <ul>
      {records.map((p) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul>
  );
}
```

```ts
const post = await client.from('posts').create({ title: 'Hello', published: false });
await client.from('posts').update(post.id, { published: true });
await client.from('posts').delete(post.id);
```

Every method and filter operator is in the [API reference](/database/api-reference/).

## Type your tables

By default records are loosely typed. Pass a `Database` type for full type safety:

```ts
type Database = {
  posts: { id: string; title: string; published: boolean; created_at: string };
};

const client = createClient<Database>();
const { records } = await client.from('posts').list(); // records: Database['posts'][]
```

> [!WARNING]
> Use a `type` alias, not an `interface`. The SDK's generic expects an index signature, which
> a named interface doesn't have — `createClient<SomeInterface>()` will not typecheck.

### Generating type definitions

You don't have to write your type definition by hand. You can use the Zeitlos CLI to generate types:

```bash
npx @zeitlosapp/cli gen types
```

**Note**: this will generate types from your local development database. Make sure those are reflecting the types you want to generate. We'll provide better tools in the future to synchronize schemas between your development and production database.

## Develop locally

```bash title="Terminal"
npx @zeitlosapp/cli dev
```

This runs the database, authentication and an MCP server on your own machine and writes the
connection details into `.env.local`, so the code above runs unchanged locally.

See [Local development](/database/local-development/) for giving the local database a
schema, generating types and troubleshooting.
