REST from your database catalog
    DocumentationCheat sheetQuestionsDownloadSource
    Questions
    • How do I...?
    Start here
    • Getting started
    • What a table becomes
    • Compared with PostgREST
    Setting up
    • Tables, columns and relations
    • Security and hooks
    • Writes and procedures
    • Prefer: return=representation
    • Node, Fastify, Express
    Reading
    • select
    • /{key}
    • relation(...)
    • order
    • limit, offset
    • relation.order, .limit
    • count
    • count(), sum(), avg()...
    • "name"
    Filtering
    • eq, neq
    • gt, gte, lt, lte
    • in
    • like, ilike
    • match, imatch
    • is
    • ov, cs, cd
    • not
    • relation.column
    • or, and
    Databases
    • Supported databases
    • Adding a database
    Reference
    • Cheat sheet
    • Errors
    Databases

    Supported databases

    What each database package supports and where they differ.

    A dialect is the package that knows one database: how to read its catalog, how to write its SQL, how to run a transaction and what its errors mean.

    PackageDatabaseEmbedsWritten keysEstimated countRow securityProcedures
    @crudy/restloom-postgresPostgreSQL 13+json_build_object, json_aggRETURNINGplanner estimateyesfunctions, scalar or set
    @crudy/restloom-mysqlMySQL 8.0+JSON_OBJECT, GROUP_CONCATlocked and read before writingnonoscalar functions
    @crudy/restloom-mssqlSQL Server 2022+JSON_OBJECT, STRING_AGGOUTPUTnosecurity policiesscalar and table functions
    @crudy/restloom-sqliteSQLite 3.44+ (better-sqlite3)json_object, json_group_arrayRETURNINGnononone

    The PostgreSQL, MySQL and SQL Server packages bring their driver and open their own connection from a URL. To tune it, pass the driver's pool options instead, or a pool you already have: a pg Pool, a mysql2/promise Pool or a connected mssql ConnectionPool. The SQLite package takes a database object, so it runs on better-sqlite3 in Node or on sql.js in a browser; install the one you use.

    Every dialect passes the same conformance suite, so a request answers the same rows on each. Regular expressions (match, imatch) need Postgres or MySQL, and array filters (ov, cs, cd) Postgres array columns; elsewhere they answer unsupported. Where else the dialects differ, the sections below say how.

    Postgres

    import { postgres } from '@crudy/restloom-postgres';
     
    const db = postgres(process.env.DATABASE_URL);
    • Transactions: every request is one transaction, READ ONLY for reads, with SET LOCAL statement_timeout from limits.timeoutMs.
    • Types: enums read as text. A domain reads as its base type.
    • Timestamps: they are written in the session's time zone, as Postgres writes them in JSON.
    • Procedures: calls use named arguments, so the order in the body does not matter.

    MySQL

    import { mysql } from '@crudy/restloom-mysql';
     
    const db = mysql(process.env.DATABASE_URL);
    • Schemas: MySQL's schemas are its databases: expose: { schemas: ['shop'] }.
    • Session: every request runs in UTC. MAX_EXECUTION_TIME applies the timeout, which MySQL enforces on reads only.
    • Writes without RETURNING: an update or delete first locks and reads the keys it will reach, then writes by key. An insert takes its keys from the body, or from AUTO_INCREMENT for a single-column key.
    • Text comparison: text compares with each column's collation, so with a case-insensitive collation eq ignores case. like is always case-sensitive and ilike never is.
    • Timestamps: written as ISO 8601. A TIMESTAMP carries +00:00; a DATETIME, which has no zone, carries none.
    • Procedures: stored functions take every argument, in any order in the body; MySQL has none returning a set.

    SQL Server

    import { mssql } from '@crudy/restloom-mssql';
     
    const db = mssql(process.env.DATABASE_URL);
    • Version: SQL Server 2022 or later, for JSON_OBJECT.
    • Timeout: the statement timeout is the pool's requestTimeout, since the driver sets it per pool, not per request.
    • Text comparison: as for MySQL, it follows the column's collation. like is case-sensitive, ilike is not.
    • Timestamps: written to the second. A datetimeoffset is written in UTC with +00:00.
    • Row security: security policies read SESSION_CONTEXT; set it in the transaction hook with sp_set_session_context.
    • Procedures: scalar and table-valued functions; T-SQL does not expose parameter defaults, so every argument is required. A T-SQL function cannot change data.

    SQLite

    npm install @crudy/restloom-sqlite better-sqlite3
    import Database from 'better-sqlite3';
    import { sqlite } from '@crudy/restloom-sqlite';
     
    const db = sqlite(new Database('app.db'));
    • Schemas: the main database is main, and an attached database is a schema by its name.
    • Connection settings: foreign keys and case-sensitive LIKE are turned on for the connection.
    • One connection: requests run one transaction at a time, queued.
    • Limits of SQLite: it cannot stop a running statement, so limits.timeoutMs does not apply. It has no functions to call as procedures.
    • Booleans: stored as 0 and 1, answered as true and false.

    Changing how SQL is written

    Every piece of SQL comes from one overridable method of the dialect's renderer. To change one, subclass it and pass it in:

    import { PostgresRenderer, postgres } from '@crudy/restloom-postgres';
     
    class Renderer extends PostgresRenderer {
        like(expr: string, pattern: string, insensitive: boolean) {
            return insensitive ? `unaccent(${expr}) ILIKE unaccent(${pattern})` : super.like(expr, pattern, false);
        }
    }
     
    const db = postgres(process.env.DATABASE_URL, { renderer: new Renderer() });

    The pieces are listed in writing a dialect.

    PreviousEither-orNext Adding a database
    restloom - a REST API generated from your database catalog
    DocumentationDownloadSource