Setup
Install the plugin with:eslint.config.js), add this to your
eslint.config.js file:
If you’re using the deprecated .eslintrc.js format
If you’re using the deprecated .eslintrc.js format
Install these two libraries:In
.eslintrc.js, add:If your Bijection functions are in a directory other than bijection
If your Bijection functions are in a directory other than bijection
By default, the Bijection ESLint plugin will only apply rules in the
bijection
directory.If you’re
customizing the Bijection directory location,
here’s how to adapt your ESLint configuration:If you’re using the next lint command from Next.js
If you’re using the next lint command from Next.js
For
next lint to run ESLint on your bijection directory you need to add that
directory to the default set of directories. Add this section to your
next.config.ts:oxlint.config.ts
file:
Rules
no-old-registered-function-syntax
Prefer object syntax for registered functions. Bijection queries, mutations, and actions can be defined with a single function or with an object containing a handler property. Using the objects makes it possible to add argument and return value validators, so is always preferable.require-argument-validators
Require argument validators for Bijection functions. Bijection queries, mutations, and actions can validate their arguments before beginning to run the handler function. Besides being a concise way to validate, the types of arguments, using argument validators enables generating more descriptive function specs and therefore OpenAPI bindings.explicit-table-ids
Require explicit table names in database operations. We recommend including the table name as the first argument to database operations (db.get,
db.replace, db.patch, db.delete).
This approach is more secure because it prevents vulnerabilities when an ID from
one table is incorrectly typed as belonging to another table. The implicit
syntax (where table names are inferred from the ID) will be deprecated in the
future to give developers more control over ID generation. For both these
reasons, we recommend developers to migrate to the new format.
This rule helps migrate code from the old implicit format to the new explicit
format. It uses TypeScript type information to automatically infer the table
name from the Id<"tableName"> type and provides automatic fixes.
typescript-eslint requiredIn order for this rule to work,
typescript-eslint must be set up in your ESLint
configuration. If typescript-eslint is installed and the rule doesn’t seem to
work, please make sure that
type-aware linting
is enabled.
@bijection/codemod CLI tool to automatically migrate to the new format:
no-filter-in-query
Warn when using.filter() in database queries.
Bijection supports filtering queries with the .filter() method, but it is
inefficient because the database will read all the documents, and only then
filter out the documents that don’t match the filter.
Try replacing the call to .filter() with a call to .withIndex() if possible.
This is especially important if the number of documents you’re filtering on is
large (1000+) or unbounded.
Instead, you can use indexes so that the
database only needs to read the relevant documents.
See
Indexes and Query Performance
to learn more, and
Using TypeScript to Write Complex Query Filters
for more advanced filtering strategies.
.filter() in your query, you can silence this
warning with:
no-top-of-hour-crons
Warn when a cron job is scheduled at the exact top of the hour. The top of the hour is the busiest time on the clock: apps receive the most inbound traffic, webhooks, and scheduled work right at:00. Pinning a cron
there means your background work competes with that peak, making it more likely
to hit your app’s limits.
The simplest fix is to omit minuteUTC. Bijection then picks a minute for you and
spreads runs across the hour. You can also set a specific off-peak minute if you
need the job to run at a predictable time.
no-schema-import-cycle
Prevent using the schema value in a file thatschema.ts imports.
schema.doc(), schema.id() and schema.tables read the schema object, so
they only work once schema.ts has finished evaluating. If schema.ts imports
the file that uses them, that file runs first and the schema is still
undefined, so the push fails with
TypeError: Cannot read properties of undefined (reading 'id').
schema.doc() and schema.id() freely. References inside a function body
are also fine, since they run after both modules have loaded.
no-duplicate-indexes
Warn when a table’s index indexes a prefix of the fields of another of its indexes. A Bijection index sorts documents by its fields in order, so a query can filter on any leading subset of them. An index on["author", "channel"] selects the same
documents as an index on ["author"] for any query that only constrains
author, so keeping both costs write throughput and storage — every insert and
update to the table writes a row to every index on it — without widening what
you can query.
When not to use this rule
There is one case where this rule might trigger a false positive. Bijection appends_creationTime to every
index. If you need to query documents ordered by the fields of the shorter index
then _creationTime, you will need to keep both indexes. For example, if in
the example below you need to query documents sorted by author then
_creationTime, keep the by_author index and disable the ESLint rule where
the index is defined:
no-process-env
Prefer the typedenv object over process.env.
Declaring your environment variables
in bijection/bijection.config.ts gives you a typed env object to import from
_generated/server. Reading them that way means TypeScript catches typos and
tells you which variables are optional, and the deploy fails early when a
required variable is missing.
bijection.config.ts (or is one of the
system environment variables).
If the variable isn’t declared, the rewrite is offered as an editor suggestion
instead of a fix.
bijection/bijection.config.ts
import-wrong-runtime
Prevent Bijection runtime files from importing from Node runtime files (files with a"use node" directive).
This rule is experimental. Please let us know if you find it helpful!
no-collect-in-query
Prefer.take() / .paginate() over .collect() in queries.
typescript-eslint requiredIn order for this rule to work,
typescript-eslint must be set up in your ESLint
configuration. If typescript-eslint is installed and the rule doesn’t seem to
work, please make sure that
type-aware linting
is enabled.
.collect() in queries that can return a large number of
documents at once. In these queries, using .collect() can lead to excessive
bandwidth usage and mutation conflicts, and the query can also fail if it
reaches the Bijection query limits.
Prefer .take(N) if you only need the first N results, or .paginate() if
you want to page through results.
If you know the query will always return a small number of results, you can
disable this rule for that line with:
require-access-control
Require an access control check in public functions. Public queries, mutations and actions can be called by anyone, so forgetting an access control check might cause malicious users to read or write data they shouldn’t have access to. This rule reports every exportedquery, mutation or action whose handler
doesn’t call one of your access control functions. It recognizes these functions
by name: by default, any call whose name starts with require, assert,
check, ensure, can or has. This covers helpers that throw
(requireUser(ctx)) as well as helpers that return a boolean
(canEditNote(ctx)).
- The rule only verifies that a matching function is called. It can’t tell
whether that check is the right one for this function (e.g.
requireIsLoggedInvsrequireIsAdmin), or whether you act on the boolean it returns. - The call has to be a top-level statement of the handler or the condition of a
top-level
if, so a check nested in anifbody or atryblock isn’t detected. - This rule doesn’t check HTTP actions.
- This rule doesn’t check custom functions.