# Built-in: lints (/docs/orm/next/middleware/built-in-lints)

> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.

The lints middleware inspects each query's structure before it runs and blocks or warns on risky shapes.

Location: ORM > Next > Middleware > Built-in: lints

The `lints` middleware inspects the structure of every query before it executes and blocks or warns on shapes that are usually mistakes, like a `DELETE` with no `WHERE` clause.

Register it on the runtime; the defaults are useful without configuration:

```ts title="src/prisma/db.ts"
import postgres from '@prisma-next/postgres/runtime';
import { lints } from '@prisma-next/sql-runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = postgres<Contract>({
  contractJson,
  url: process.env['DATABASE_URL']!,
  middleware: [lints()],
});
```

`lints` is SQL-family middleware (it declares `familyId: 'sql'`), so it registers on PostgreSQL runtimes. It ships with `@prisma-next/sql-runtime`, which comes in through `@prisma-next/postgres`.

## The rules [#the-rules]

Lints run in `beforeExecute`, against the query plan's AST. Each rule has a severity: `error` throws and blocks the query before it reaches the database, `warn` logs through the runtime log and lets the query run.

| Rule                 | Code                        | Default severity | Fires when                                         |
| -------------------- | --------------------------- | ---------------- | -------------------------------------------------- |
| DELETE without WHERE | `LINT.DELETE_WITHOUT_WHERE` | `error`          | A `DELETE` has no `WHERE` clause                   |
| UPDATE without WHERE | `LINT.UPDATE_WITHOUT_WHERE` | `error`          | An `UPDATE` has no `WHERE` clause                  |
| No LIMIT             | `LINT.NO_LIMIT`             | `warn`           | A `SELECT` has no `LIMIT` clause                   |
| SELECT star          | `LINT.SELECT_STAR`          | `warn`           | A query selects all columns instead of naming them |

A warning is emitted as a structured event on the runtime's log:

```text title="Lint warning"
warn {
  code: 'LINT.NO_LIMIT',
  message: 'Unbounded SELECT may return large result sets',
  details: { table: 'user' }
}
```

> [!NOTE]
> The `postgres(...)` client does not expose a way to attach a log sink yet, so `warn` findings are not printed anywhere by default; only `error` severities are observable, because they block the query. Treat `warn` rules as forward-looking until the client accepts a logger.

## Configure severities [#configure-severities]

Raise or lower any rule per project through `severities`. For example, treat unbounded selects as hard failures in a service where every list endpoint must paginate:

```ts title="src/prisma/db.ts"
lints({
  severities: {
    noLimit: 'error',
    selectStar: 'warn',
  },
});
```

The severity keys are `deleteWithoutWhere`, `updateWithoutWhere`, `noLimit`, and `selectStar`, plus `readOnlyMutation` for the raw-SQL guardrail described below. The options type also accepts `unindexedPredicate`, but no rule emits that finding in v0.14.

## Raw SQL [#raw-sql]

Queries built with the ORM client or the SQL query builder carry a typed AST, and the rules above inspect it structurally. Raw SQL does not have that structure, so `lints` falls back to text heuristics for raw plans: it flags `select *`, a missing `LIMIT`, and a mutation smuggled through a raw query marked read-only (`LINT.READ_ONLY_MUTATION`). The raw heuristics carry their own defaults: `select *` escalates to `error` for raw plans (stricter than the AST rule's `warn`), a missing `LIMIT` stays a warning, and a read-only-intent mutation is an `error`. The `severities` keys above override raw findings too. Control the fallback with `fallbackWhenAstMissing`:

```ts title="src/prisma/db.ts"
lints({ fallbackWhenAstMissing: 'raw' }); // default: heuristic checks on the SQL text
lints({ fallbackWhenAstMissing: 'skip' }); // skip linting for plans without an AST
```

Heuristics can misread SQL that a structural check would classify correctly, so treat raw-SQL lint findings as a prompt to look at the query rather than a verdict.

## Common gotchas [#common-gotchas]

> [!WARNING]
> `LINT.DELETE_WITHOUT_WHERE` and `LINT.UPDATE_WITHOUT_WHERE` default to `error`, so a deliberate full-table mutation (for example a backfill) will be blocked. For a one-off script, either add a tautological but explicit `WHERE` clause or register `lints` with that rule set to `warn` in that script's runtime setup. Lowering the severity globally gives up the protection everywhere.

## See also [#see-also]

* [How middleware works](https://www.prisma.io/docs/orm/next/middleware/how-middleware-works)
* [Built-in: budgets](https://www.prisma.io/docs/orm/next/middleware/built-in-budgets) for row-count and latency ceilings
* [Authoring custom middleware](https://www.prisma.io/docs/orm/next/middleware/authoring-custom-middleware)

## Related pages

- [`Authoring custom middleware`](https://www.prisma.io/docs/orm/next/middleware/authoring-custom-middleware): Build, register, and run your own Prisma Next middleware, step by step, starting with a query logger.
- [`Built-in: budgets`](https://www.prisma.io/docs/orm/next/middleware/built-in-budgets): The budgets middleware caps row counts and makes over-latency queries visible.
- [`Built-in: cache`](https://www.prisma.io/docs/orm/next/middleware/built-in-cache): The cache middleware serves repeated reads from an in-memory store, opted in per query with a cache annotation.
- [`How middleware works`](https://www.prisma.io/docs/orm/next/middleware/how-middleware-works): Middleware runs your code before and after every query, so one policy can cover your whole app.