---
title: "Linting"
description: "Convex's official lint rules for your functions directory, as one ESLint preset."
canonical_url: "https://nuxt-backend.dev/docs/ship/test/linting"
---
# Linting

> Convex's official lint rules for your functions directory, as one ESLint preset.

`nuxt-backend/eslint` applies [Convex's own lint rules](https://docs.convex.dev/eslint) to your functions directory and keeps ESLint out of the generated code.

## Setup

Add `@nuxt/eslint` (the `create nuxt` modules prompt offers it too) and the Convex plugin:

```bash
npx nuxt module add eslint
npm install -D @convex-dev/eslint-plugin
```

Then pass the preset to the config `@nuxt/eslint` generated:

```js [eslint.config.mjs]
import withNuxt from './.nuxt/eslint.config.mjs'
import { backendEslint } from 'nuxt-backend/eslint'

export default withNuxt(backendEslint())
```

`@convex-dev/eslint-plugin` is an optional peer of this package, so an app that does not lint never installs it. Without it, the config fails to load and names the missing package.

## What it adds

`backendEslint()` returns two flat-config entries:

- **nuxt-backend:convex** applies the rules below to `backend/**/*.ts`, leaving out test files.
- **nuxt-backend:generated** ignores every `_generated` folder under `backend/`. Convex's generated files start with `/* eslint-disable */`, which ESLint would otherwise report as an unused directive.

## Rules

<table>
<thead>
  <tr>
    <th>
      Rule
    </th>
    
    <th>
      Level
    </th>
    
    <th>
      What it catches
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        @convex-dev/no-old-registered-function-syntax
      </code>
    </td>
    
    <td>
      error
    </td>
    
    <td>
      <code>
        query(async (ctx) => …)
      </code>
      
       instead of <code>
        query({ args, handler })
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/require-args-validator
      </code>
    </td>
    
    <td>
      error
    </td>
    
    <td>
      A function without an <code>
        args
      </code>
      
       validator
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/explicit-table-ids
      </code>
    </td>
    
    <td>
      error
    </td>
    
    <td>
      <code>
        ctx.db.get(id)
      </code>
      
       instead of <code>
        ctx.db.get('table', id)
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/no-filter-in-query
      </code>
    </td>
    
    <td>
      warn
    </td>
    
    <td>
      <code>
        .filter()
      </code>
      
       on a database query, where an index would do
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/no-top-of-hour-crons
      </code>
    </td>
    
    <td>
      warn
    </td>
    
    <td>
      A cron at minute 0, when everyone else's run too
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/no-schema-import-cycle
      </code>
    </td>
    
    <td>
      error
    </td>
    
    <td>
      The schema value used in a file the schema imports
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/no-duplicate-indexes
      </code>
    </td>
    
    <td>
      error
    </td>
    
    <td>
      An index whose fields start another index
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        @convex-dev/no-process-env
      </code>
    </td>
    
    <td>
      error
    </td>
    
    <td>
      <code>
        process.env
      </code>
      
       instead of the typed <code>
        env
      </code>
      
       from <code>
        _generated/server
      </code>
    </td>
  </tr>
</tbody>
</table>

The levels are those of the plugin's own recommended config, and `BACKEND_ESLINT_RULES` exports the table. Three rules stay off, as they do there: `require-access-control`, `import-wrong-runtime` and `no-collect-in-query`. The scaffold's [function builders](https://nuxt-backend.dev/docs/auth/access/functions) already check access, so the first one would mostly repeat them.

A scaffolded app passes every rule. The package's test suite lints both scaffolds with this preset on every change.

## Options

```js [eslint.config.mjs]
export default withNuxt(backendEslint({
  // The `functions` entry of convex.json. Default: 'backend'.
  functionsDir: 'convex',
  // Levels that override or extend the table above.
  rules: { '@convex-dev/no-collect-in-query': 'warn' },
}))
```

## Without `@nuxt/eslint`

The rules parse TypeScript. `withNuxt` sets the parser up. A standalone flat config installs ESLint and the parser itself, next to the Convex plugin:

```bash
npm install -D eslint typescript-eslint @convex-dev/eslint-plugin
```

Then it adds the parser before the preset:

```js [eslint.config.mjs]
import tseslint from 'typescript-eslint'
import { backendEslint } from 'nuxt-backend/eslint'

export default [
  { files: ['**/*.ts'], languageOptions: { parser: tseslint.parser } },
  ...backendEslint(),
]
```


## Sitemap

See the full [sitemap](https://nuxt-backend.dev/sitemap.md) for all pages.
