Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iterate/sqlfu/llms.txt

Use this file to discover all available pages before exploring further.

sqlfu/lint-plugin ships an ESLint plugin that enforces the SQL First model at the editor level. Your .sql file is the source of truth, not an inline string. The plugin runs under ESLint flat config on both TypeScript and JavaScript source (inline SQL template literals) and standalone .sql files via an ESLint processor.

Quick setup

Add the recommended preset to your ESLint flat config:
eslint.config.js
import sqlfu from 'sqlfu/lint-plugin';

export default [
  ...sqlfu.configs.recommended,
];
The recommended preset enables:
  • sqlfu/query-naming and sqlfu/format-sql on TS/JS files
  • The sqlfu/sql processor so .sql files are lintable
  • sqlfu/generated-query-freshness to check that generated wrappers are current

VS Code settings

Make sure the ESLint extension validates .sql files as well as your JS/TS files. Without this, command-line linting works but SQL lint errors may not appear in the editor:
.vscode/settings.json
{
  "eslint.validate": ["markdown", "javascript", "typescript", "sql"]
}

Rules

sqlfu/query-naming

Flags inline SQL template literals that duplicate a checked-in .sql file.
// flagged: this SQL matches sql/get-posts.sql exactly
const posts = client.all(sql`select id, slug from posts where published = 1`, {});

// correct: use the generated wrapper function
import {getPosts} from './sql/.generated/get-posts.sql.ts';
const posts = await getPosts(client, {limit: 10});
Your filename is your query’s identity. An inline duplicate loses the query name, the generated TypeScript types, and the observability metadata (OpenTelemetry span name, Sentry tag, etc.). The rule catches the case where you paste a query inline rather than pointing at the .sql file. The rule fires on client.all, client.run, client.iterate, and client.sql`...` calls where the template has no interpolations and the normalized SQL matches a file under your project’s queries directory.

sqlfu/format-sql

Flags SQL that does not match sqlfu’s formatter output, on both inline template literals and standalone .sql files.
-- flagged
SELECT Id, Slug FROM Posts WHERE Published = 1

-- correct (lowercase, sqlfu style)
select id, slug from posts where published = 1
eslint --fix '**/*.sql' reformats .sql files in place. The same rule autofixes inline template bodies in TS/JS.

sqlfu/generated-query-freshness

Flags checked-in .sql query files whose generated manifest or wrapper is missing or stale.
sql/list-posts.sql                # source query
sql/.generated/queries.ts         # generated query barrel and manifest
sql/.generated/list-posts.sql.ts  # generated by sqlfu generate
The rule compares the exact file contents with the sourceSql emitted in .generated/queries.ts. If the manifest is missing, the source query is absent from it, the wrapper file is missing, or the SQL text no longer matches exactly, lint reports that sqlfu generate needs to be run. When ESLint also lints .generated/queries.ts, the same rule reconciles the whole generated query set and reports orphaned generated wrappers left behind after a source query is deleted.
There is no autofix for sqlfu/generated-query-freshness. Generation may require schema analysis and writes multiple files. Run sqlfu generate to fix the failures.

Manual configuration

If you need to wire the rules individually rather than using the preset:
eslint.config.js
import sqlfu from 'sqlfu/lint-plugin';

export default [
  {
    files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'],
    plugins: {sqlfu},
    rules: {
      'sqlfu/query-naming': 'error',
      'sqlfu/format-sql': 'error',
    },
  },
  {
    files: ['**/*.sql'],
    plugins: {sqlfu},
    processor: 'sqlfu/sql',
  },
  {
    files: ['**/*.sql/**/*.js'],
    plugins: {sqlfu},
    rules: {
      'sqlfu/format-sql': 'error',
      'sqlfu/generated-query-freshness': 'error',
    },
  },
];

Rule options

The rules accept optional configuration objects:
eslint.config.js
'sqlfu/query-naming': ['error', {
  queriesDir: './sql',                         // override the queries directory
  clientIdentifierPattern: /^(client|db)$/u,  // customize the client identifier regex
}]

'sqlfu/generated-query-freshness': ['error', {
  queriesDir: './sql',  // override the queries directory
}]
Both queriesDir options default to the value of queries in your sqlfu.config.ts. Override them here only if your ESLint config runs in a different context than the sqlfu config.

The sqlfu/sql processor

The sqlfu/sql processor makes .sql files lintable by wrapping them in a tagged template literal so ESLint’s JS parser can process them. It is included automatically by the recommended preset. You only need to configure it separately if you are not using the preset.
The recommended config is a reference implementation, not a locked-forever API. The stable contracts are the rule names (sqlfu/query-naming, sqlfu/format-sql, sqlfu/generated-query-freshness) and their options schema. If your team’s ESLint setup needs a different shape, copy the preset and adjust it.

Build docs developers (and LLMs) love