# Plugins overview

> Package setup logic, query transforms, typed operation args, and reusable client/model extensions - without wrapping better() yourself.

URL: https://better-drizzle.com/docs/plugins/overview

Plugins let you bundle behavior that would otherwise be scattered across services: setup logic, query transforms, lifecycle hooks, extra delegate methods, and even new typed arguments on the built-in operations.

```ts
import { better } from 'better-drizzle';
import { recommended, rules } from 'better-drizzle/rules';
import { zod as betterZod } from 'better-drizzle/zod';
import { timestamps } from 'better-drizzle/timestamps';
import { softDelete } from 'better-drizzle/soft-delete';

const client = better(db, {
	plugins: [
		rules(recommended({ noRawUnsafe: true })),
		betterZod({ validate: { create: true, update: true } }),
		timestamps({ createdAt: 'createdAt', updatedAt: 'updatedAt' }),
		softDelete({ column: 'deletedAt' }),
	],
});

await client.users.delete({ where: { id: 1 } }); // soft delete
await client.users.findMany({ deleted: 'only' }); // typed plugin arg
await client.users.restore({ where: { id: 1 } }); // plugin-added method
```

## Official plugins

- [ESLint](/docs/plugins/eslint): Add static guardrails in the IDE and CI for direct Better Drizzle call
sites, with rules and options documented on the package page.

- [Rules](/docs/plugins/rules): Add runtime guardrails for raw SQL, destructive writes, pagination,
locking, and request context policies.

- [Timestamps](/docs/plugins/timestamps): Manage `createdAt` / `updatedAt` consistently on create, update, and
upsert - or stay out of the way when the database owns them.

- [UUIDs](/docs/plugins/uuid): Fill UUIDv7 or UUIDv4 ids on create, make them optional in create types,
and store them as 16 bytes on MySQL and SQLite with `buuid()`.

- [Zod](/docs/plugins/zod): Generate per-table Zod schemas, expose them on `$zod`, and validate
payloads, query args, and results through plugin hooks.

- [ATA](/docs/plugins/ata): Experimental. A faster alternative to Zod: describe tables as JSON
Schema, expose them on `$ata`, and validate operations with compiled ata
validators.

- [Soft delete](/docs/plugins/soft-delete): Turn `delete()` into a recoverable state change, filter deleted rows by
default, and add `restore()` helpers.

- [Cache](/docs/plugins/cache): Experimental. Cache opted-in reads in Redis, invalidated after writes and commits
without scanning keys.

- [Tenant](/docs/plugins/tenant): Experimental. Scope every query, relation, write, and upsert to a tenant,
fail closed without one, and add PostgreSQL RLS settings.

| Plugin                | Good fit when                                                     | Main behavior                                                                       |
| --------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| ESLint                | you want IDE and CI feedback before code runs                     | flags the statically-checkable subset of Better Drizzle guardrails                  |
| Rules                 | you want runtime guardrails around repository usage               | validates raw SQL, destructive writes, limits, lock usage, and context requirements |
| Zod                   | you want generated runtime schemas for inputs and results         | builds per-table Zod schemas and validates Better Drizzle operations                |
| ATA (experimental)    | you want faster validation, or JSON Schema for inputs and results | builds per-table JSON Schemas and validates operations with compiled validators     |
| Timestamps            | you want managed `createdAt` / `updatedAt`                        | fills timestamp columns on writes                                                   |
| UUIDs                 | ids are UUIDs generated by the application                        | fills UUIDv7 or UUIDv4 ids on create and makes them optional in create types        |
| Soft delete           | rows should stay recoverable and hidden by default                | rewrites deletes, filters root and nested reads, adds restore helpers               |
| Cache                 | hot reads should skip the database                                | caches opted-in reads in Redis and invalidates them after writes                    |
| Tenant (experimental) | tenants share tables and must never see each other's rows         | filters every statement by tenant columns, stamps them on create, and fails closed  |

`better-drizzle/eslint` is intentionally separate from the runtime plugin pipeline. It mirrors the statically-checkable subset of `better-drizzle/rules` for direct call sites, while runtime-only concerns such as transaction state, merged `meta`, and dialect behavior stay in `better-drizzle/rules`.

## What a plugin can do

* declare a stable `id`, `name`, `version`, and `description`
* add **typed operation args** (e.g. `deleted`, `mode`) that flow into transforms and hooks
* **transform** an operation before it runs (rewrite `where`, inject data, skip it)
* **intercept** execution to return a result without SQL or act after it runs
* observe CRUD, query, raw, and transaction **lifecycle hooks**
* **extend the client** with new top-level methods
* **extend each delegate** with model-specific helpers
* carry per-call **state** via `$withState(...)`

See [writing a plugin](/docs/plugins/writing-plugins) for the full API.

## Composition and order

Plugins run in array order. If two plugins transform the same operation, the earlier one runs first:

```ts
import { softDelete } from 'better-drizzle/soft-delete';
import { timestamps } from 'better-drizzle/timestamps';

// timestamps() transforms each write before softDelete() sees it
const client = better(db, {
	plugins: [timestamps(), softDelete()],
});
```

Order matters when:

* one plugin adds fields another plugin expects
* one plugin changes a visibility or mode flag
* a lifecycle hook emits audit events that depend on already-transformed data

> **Keep plugins narrow:** Strong plugins do one thing well - soft delete, timestamps, audit metadata,
> multitenancy filters, a few model helpers. If a plugin starts replacing most
> of the repository behavior, it is probably too broad.
