> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vertz.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> @vertz/server — entity-driven backend with auto-generated CRUD, access control, and SDK generation

`@vertz/server` is the backend layer of the Vertz stack. You define entities with schemas, access rules, and hooks — the framework generates typed CRUD routes, validates inputs, enforces access control, and produces a typed client SDK. No boilerplate controllers, no manual route wiring.

## How it works

<Steps>
  <Step title="Define your entity">
    Declare the data model, who can do what, and any side effects (hooks). One object, one place.
  </Step>

  <Step title="Framework generates routes">
    CRUD endpoints are auto-generated with validation, access enforcement, and error handling built
    in.
  </Step>

  <Step title="SDK is generated">
    A fully typed client SDK is produced from your entity definitions — used by the UI layer with
    `query()` and `form()`.
  </Step>
</Steps>

## What's included

| Feature                       | Description                                                                      |
| ----------------------------- | -------------------------------------------------------------------------------- |
| **Entities**                  | Declarative data models with CRUD, access rules, hooks, and custom actions       |
| **Domains**                   | Group entities and services into bounded contexts with automatic route prefixing |
| **Field & relation exposure** | Control which fields, filters, and relations are exposed in query responses      |
| **Services**                  | Standalone operations not tied to a single entity                                |
| **Access control**            | Deny-by-default, per-operation access rules with row-level checks                |
| **Environment**               | Validated, typed, frozen config via `createEnv()`                                |
| **Authentication**            | Email/password, JWT sessions, refresh rotation, OAuth providers                  |
| **Middleware**                | Request pipeline with typed context propagation                                  |
| **SDK generation**            | Auto-generated typed client from entity definitions                              |

## Quick example

```ts theme={null}
import { createServer, entity, rules } from '@vertz/server';
import { createDb, d } from '@vertz/db';

const tasksModel = d.model(
  d.table('tasks', {
    id: d.uuid().primary({ generate: 'cuid' }),
    title: d.text(),
    status: d.enum('task_status', ['todo', 'in_progress', 'done']).default('todo'),
    createdAt: d.timestamp().default('now').readOnly(),
  }),
);

const tasks = entity('tasks', {
  model: tasksModel,
  access: {
    list: rules.public,
    get: rules.public,
    create: rules.public,
    update: rules.public,
    delete: rules.public,
  },
});

const db = createDb({
  url: process.env.DATABASE_URL!,
  models: { tasks: tasksModel },
});

createServer({ entities: [tasks], db }).listen({ port: 3000 });
```

This generates:

```
GET    /api/tasks          → list (with filtering, pagination)
GET    /api/tasks/:id      → get
POST   /api/tasks          → create
PATCH  /api/tasks/:id      → update
DELETE /api/tasks/:id      → delete
```

No route handlers to write. Validation, access checks, and error responses are handled automatically.

## API prefix

By default, all routes are mounted under `/api/`. You can change this with the `apiPrefix` option:

```ts theme={null}
createServer({
  entities: [tasks],
  db,
  apiPrefix: '/v1',
});
```

This generates routes under `/v1/` instead:

```
GET    /v1/tasks          → list
POST   /v1/tasks          → create
GET    /v1/auth/signin    → sign-in page
...
```

### Defaults and normalization

| Input        | Resolved prefix |
| ------------ | --------------- |
| `undefined`  | `/api`          |
| `'/v1'`      | `/v1`           |
| `'/api/v2/'` | `/api/v2`       |
| `'/'`        | `''` (root)     |

Trailing slashes are stripped automatically. A bare `'/'` is treated as an empty prefix (root mount).

### API-only apps

If your server has no UI layer, you can remove the prefix entirely:

```ts theme={null}
createServer({
  entities: [tasks],
  db,
  apiPrefix: '',
});
```

Routes mount at the root: `GET /tasks`, `POST /tasks`, etc.

<Warning>
  Empty `apiPrefix` is only allowed in API-only mode. Full-stack apps (with SSR) require a non-empty
  prefix so the server can distinguish API requests from page navigations.
</Warning>

### Auth routes

Auth routes automatically follow the prefix. If `apiPrefix` is `'/v1'`, auth routes are served at `/v1/auth/*` and session cookies are scoped to `Path=/v1/auth/...`.

### Cloudflare Workers

When deploying to Cloudflare, `createHandler` reads `apiPrefix` from your server automatically. You only need to set it explicitly if you want to override:

```ts theme={null}
createHandler({
  app: (env) => createServer({ apiPrefix: '/v1' }),
  ssr: { module: app },
  // apiPrefix is auto-read from app — no need to repeat it
});
```

See the [Cloudflare Workers guide](/guides/deploy/cloudflare#custom-apiprefix) for details.

## Core principles

### Entity-driven

Entities are the central abstraction. A single entity definition covers the data model, validation, access control, lifecycle hooks, and custom actions. Everything flows from the entity.

### Deny-by-default

No access rule = no route generated. You explicitly declare who can perform each operation. There's no "open by default" — you opt in to access, not out.

### Type-safe end-to-end

Types flow from your schema definition through the server, into the generated SDK, and down to the UI layer. If it compiles, the types are correct across the entire stack.

## Guides

<CardGroup cols={2}>
  <Card title="Entities" icon="database" href="/guides/server/entities">
    Models, access rules, hooks, and custom actions.
  </Card>

  <Card title="Domains" icon="layer-group" href="/guides/server/domains">
    Group entities and services into bounded contexts with route prefixing.
  </Card>

  <Card title="Fields, Relations & Filters" icon="filter" href="/guides/server/entity-exposure">
    Control which fields, filters, and relations clients can query.
  </Card>

  <Card title="Authentication" icon="shield-check" href="/guides/server/auth">
    Email/password, JWT sessions, refresh tokens, and session management.
  </Card>

  <Card title="OAuth Providers" icon="key" href="/guides/server/oauth">
    Add Google, GitHub, or Discord sign-in.
  </Card>

  <Card title="Services" icon="bolt" href="/guides/server/services">
    Standalone operations and cross-entity workflows.
  </Card>

  <Card title="Environment" icon="lock" href="/guides/env">
    Validate and type-check env variables with `createEnv()`.
  </Card>
</CardGroup>
