---
title: Drizzle database service
description: Understand how the Drizzle integration turns table definitions into typed CRUD, validation schemas, model extensions, and a shared application context.
pagination: false
sidebar:
  icon: database
---

Use the Drizzle integration for relational tables and Drizzle's TypeScript query builder. Use `@relkit/drizzle` when you want relational tables and Drizzle's TypeScript query builder.
This page describes that integration's behavior; return to [Database](/docs/modules/database) for the
adapter-neutral architecture.

> **Before you begin**
>
> Before using these examples, complete [database installation and
> configuration](/docs/modules/database/define), including the selected adapter, native ORM, tooling
> and application-owned patch.

## Define once and reuse throughout the app

Export `defineDrizzleService(...)` from `src/services/database/service.ts`:

```ts title="src/services/database/service.ts"
/** Owns lazy SQLite registration and disposal, supplying users-model extensions and an inactive-user lookup override. */
import { Database } from "bun:sqlite";
import { defineDrizzleService } from "@relkit/drizzle";
import { drizzle } from "drizzle-orm/bun-sqlite";
import * as schema from "./schema/index.js";
import usersModel from "./models/users.model.js";

export default defineDrizzleService({
  schema,
  client: ({ env }) => drizzle({ client: new Database(env.DATABASE_PATH) }),
  models: { users: usersModel },
  overrides: {
    users: {
      findOne: async ({ args, base }) => {
        const user = await base(args);
        return user?.active === false ? null : user;
      },
    },
  },
  dispose: (database) => database.$client.close(),
});
```

The factory connects to the database. It does not require handwritten table-creation statements.
Define the tables in TypeScript and apply their changes through [Drizzle Kit migrations](/docs/modules/database/migrations)
before starting the application.

The optional `models` and `overrides` in this example customize behavior. Remove those two options and
the model import if you only need the generated base operations.

## Understand what is automatic

For every table exported through `schema`, RELKIT provides:

| Generated surface                                | Example                                        | Purpose                                                      |
| ------------------------------------------------ | ---------------------------------------------- | ------------------------------------------------------------ |
| Six typed CRUD methods                           | `context.database.users.insert({ data })`      | Read and mutate records without writing a repository wrapper |
| Select, insert, and update Zod schemas           | `context.database.zodSchemas.users.insert`     | Validate data against the table shape when explicitly called |
| Model extension methods                          | `context.database.users.byEmailDomain(domain)` | Expose the custom methods you register                       |
| Transaction-bound context                        | `context.database.transaction(callback)`       | Compose related database operations                          |
| Compiler context declarations and graph metadata | `.relkit/generated/context-registry.d.ts`      | Give application code typed access to the discovered service |

Base CRUD and schemas exist even for a table with no custom model file. Changing a table updates the
derived TypeScript types and validation schemas; regenerate context declarations when registration
changes, and migrate the physical database separately. See [Generated CRUD and schemas](/docs/modules/database/generated-schemas)
for the exact data shapes, customization, and validation boundaries.

This does not generate HTTP CRUD endpoints, public function contracts, authorization rules, seed data,
or database migrations. Add [business functions](/docs/learn/concepts/functions) and
[routes](/docs/modules/http) deliberately.

## Follow discovery and activation

The compiler discovers the service, inspects its table metadata, and generates application context types.
The runtime then invokes `client({ env })` during startup and injects the resulting database context
into business functions. Normal shutdown calls `dispose`.

There is no `dependencies.database` entry and no database provider profile to add to
`relkit.config.ts`. Do not wrap this descriptor in `defineService`. The database service supplies
persistence; the [Users service](/docs/modules/service/define) still owns its business operations.

Only one Drizzle service is currently supported per application. Its schema must contain at least one
table and use one dialect: SQLite, PostgreSQL, or MySQL. These are current integration constraints,
not promises about future MongoDB or Prisma integrations.

## Choose the customization point

Use [schema definitions](/docs/modules/database/schema) for stored shapes and constraints,
[custom models](/docs/modules/database/models) for named Drizzle queries, and
[overrides](/docs/modules/database/overrides) to change an existing CRUD method. Keep caller permissions and
public input/output in the owning business function.

Continue to [Define a Drizzle service](/docs/modules/database/define) for dependencies, environment, driver setup,
and lifecycle. Exact signatures live in the [Drizzle API reference](/docs/api/drizzle).

{/* Generated by apps/docs/scripts/generate.ts. Do not edit. */}

**Reference and source**

- [`@relkit/drizzle` API](/docs/api/drizzle)
- [`@relkit/services` API](/docs/api/services)

- [`examples/data-model/src/services/database/service.ts`](https://github.com/rel-kit/relkit/blob/main/examples/data-model/src/services/database/service.ts)
