API
All of the options that are mentioned below should be configured in your eslintrc
config file under the rule "@ts-safeql/check-sql"
. Be sure to read the Configuration guide first.
useConfigFile
If set to true
, SafeQL will look for a safeql.config.ts
file in the root of your project.
{
"useConfigFile": true
}
// safeql.config.ts
import { defineConfig } from "@ts-safeql/eslint-plugin";
export default defineConfig({
connections: {
// ...
},
});
INFO
This option cannot be used with connections
.
connections
Can be either an object that represents a single connection, or an array that represents multiple connections.
In most use cases, when you have only one database connection, you can pass a single object that represents the type of the connection. For example:
{
"connections": {
"databaseUrl": "postgres://user:pass@localhost:5432/dbname",
"targets": [{ "tag": "sql" }]
}
}
When working with multiple connections, you can pass an array of connections:
{
"connections": [
{
"databaseUrl": "postgres://user:pass@localhost:5432/dbname",
"targets": [{ "tag": "sql" }]
},
{
"databaseUrl": "postgres://user:pass@localhost:5432/dbname2",
"targets": [{ "tag": "prisma.+($queryRaw|$executeRaw)" }]
}
]
}
connections.databaseUrl
The database URL that the plugin will use to infer the types of the queries, and report any errors.
INFO
- this option cannot be used with
migrationsDir
,connectionUrl
, anddatabaseName
connections.migrationsDir
The path to the directory where your database migrations are located (only .sql
migration files supported currently). For example:
{
"connections": {
"migrationsDir": "prisma/migrations"
// ...
}
}
INFO
- this option can be used with
databaseName
. - this option can be used with
watchMode
. - this option can be used with
connectionUrl
. - this option cannot be used with
databaseUrl
.
connections.watchMode
(Optional)
Whether or not to recreate the shadow database when a migration file is changed. This option is only relevant when migrationsDir
is used.
If no value is provided, then it will default to true
.
{
"connections": {
"migrationsDir": "...",
"watchMode": true
// ...
}
}
INFO
- this option must be used with
migrationsDir
. - this option can be used with
connectionUrl
. - this option can be used with
databaseName
. - this option cannot be used with
databaseUrl
.
connections.connectionUrl
(Optional)
If no value is provided, it will fallback to:
postgres://postgres:postgres@localhost:5432/postgres
When using migrations, we don't have a database URL. In order to connect to Postgres, we need to connect to an existing database. The default value is:
A connection URL is required to create a shadow database (and drop it after) to query the database metadata so it can infer the types of the queries, and report any errors.
SHADOW DATABASE
Shadow database is a database that is being used to query the migrations folder metadata. It is created and dropped automatically by SafeQL.
INFO
- this option must be used with
migrationsDir
. - this option can be used with
watchMode
. - this option can be used with
databaseName
. - this option cannot be used with
databaseUrl
.
connections.databaseName
(Optional)
The name of the shadow database that will be created and dropped automatically by SafeQL. If no value is provided, the default value is safeql_${underscore_dir_name}_{dir_path_hash}
. Read more in connectionUrl
option. For example:
{
"connections": {
"migrationsDir": "...",
"databaseName": "my_shadow_db"
// ...
}
}
INFO
- this option must be used with
migrationsDir
. - this option can be used with
watchMode
. - this option can be used with
connectionUrl
. - this option cannot be used with
databaseUrl
.
connections.targets.tag
The targets
tell SafeQL where to look for the queries. It's an array of tag
and wrapper
targets.
{
"connections": {
// ...
"targets": [
{ "tag": "sql1" }
{ "tag": "sql2" }
{ "wrapper": "conn.query" }
{ "wrapper": "conn.+(query|execute)" }
{ "wrapper": { "regex": "conn2.+(query|execute)" } }
]
}
}
connections.targets.tag
The name of the tag (which could be a string, regex, or a glob pattern using minimatch) that SafeQL will use to analyze the queries. For example:
{
"connections": {
// ...
"targets": [{ "tag": "sql" }]
// or use a glob pattern:
// "targets": [{ "tag": "prisma.+($queryRaw|$executeRaw)" }]
// or use regex:
// "targets": [{ "tag": "prisma\.($queryRaw|$executeRaw)" }]
}
}
for:
const sql = postgres();
sql`SELECT id FROM users`;
// will be fixed to
sql<{ id: number }>`SELECT id FROM users`;
TIP
If you're using Postgres.js, then be sure to check out the guide on how to use SafeQL with Postgres.js.
INFO
- this option cannot be used with
wrapper
as a sibling property.
connections.targets.wrapper
The wrapper function that receives the sql tag as an argument:
{
"connections": {
// ...
"targets": [{ "wrapper": "conn.query" }]
}
}
const conn = new Client();
conn.query(...);
INFO
- this option cannot be used with
tag
as a sibling property.
connections.targets.maxDepth
(Optional)
By default, SafeQL assumes that each wrapper has a direct descendant of an sql tag. In some cases, you may want to increase the depth of the sql tag lookup. For example:
conn.query(sql`SELECT id FROM users`); // depth = 0
conn.query([sql`SELECT id FROM users`]); // depth = 1
conn.query(...sql`SELECT id FROM users`); // depth = 1
conn.query(...[sql`SELECT id FROM users`]); // depth = 2
In this case, you can use the maxDepth
option to specify the maximum depth of the sql tag. For example:
{
"connections": {
// ...
"targets": [
{
"wrapper": "conn.query",
"maxDepth": 2
}
]
}
}
connections.targets.transform
(Optional)
Transform the end result of the query. For example, if you want to transform the result of the query to be an array of objects, you can use the following:
{
"connections": {
// ...
"targets": [
{
// ...
"transform": "{type}[]"
}
]
}
}
EXAMPLES
"{type}[]"
will transform the type to an array of the type.["colname", "x_colname"]
will replacecolname
withx_colname
in the type.["{type}[]", ["colname", x_colname"]]
will do both
connections.targets.fieldTransform
(Optional)
Transform the (column) field key. Can be one of the following:
"snake"
-userId
→user_id
"camel"
-user_id
→userId
"pascal"
-user_id
→UserId
"screaming snake"
-user_id
→USER_ID
{
"connections": {
// ...
"targets": [
{
// ...
"fieldTransform": "camel"
}
]
}
}
connections.targets.skipTypeAnnotations
(Optional)
Skip adding type annotations to the query. This is useful if you're using a library that doesn't support type annotations. Usage:
{
"connections": {
// ...
"targets": [
{
// ...
"skipTypeAnnotations": true
}
]
}
}
connections.keepAlive
(Optional)
True by default. If set to false, the connection will be closed after the query is executed. This is not recommended, and should only be used if you're sure that the connection should be closed.
connections.overrides.types
(Optional)
INFO
Please note that SafeQL won't actually parse the type, since SafeQL runs only in the tooling system (i.e, not in runtime).
Override the default type mapping. For example, if you want to use LocalDate
instead of Date
for the date
type, you can use the following:
{
"connections": {
"overrides": {
"types": {
"date": "LocalDate"
}
}
}
}
Sometimes, the TypeScript type of the parameter (sql variable) is not the same as the type of the result. For example:
import postgres from "postgres";
import { sql } from "./sql";
function run(value: postgres.Parameter<LocalDate>) {
const result = sql<{ date: LocalDate }>`SELECT ${value}`;
// ...
}
In this case, you can use the following syntax:
{
"connections": {
"overrides": {
"types": {
"date": {
// the type of the parameter (can be a regex or a glob pattern)
"parameter": "Parameter<LocalDate>",
// the generated type
"return": "LocalDate"
}
}
}
}
}
connections.overrides.columns
(Optional)
INFO
While SafeQL generally succeeds in inferring column types, it sometimes cannot, particularly with JSONB
columns where the type is not explicitly defined. Additionally, there may be scenarios where you want to enforce a specific type for a column.
{
"connections": {
"overrides": {
"columns": {
"table_name.column_name": "CustomType"
}
}
}
}