sethucms
Build on it

Database adapters

The gateway ships with these adapters. Each one is a separate package in sethucms-adapters, passes the same conformance suite, and is checked by the same guard layer (field allowlist, parameters only, permission engine).

AdapterKindPackageDriverTested against
Memorymemory@sethucms/adapter-memorynonein-process
PostgreSQLpostgres@sethucms/adapter-postgrespgPostgreSQL 16
SQLitesqlite@sethucms/adapter-sqliteNode's built-in node:sqlitereal SQLite files
MySQL / MariaDBmysql@sethucms/adapter-mysqlmysql2 (peer)MariaDB 10.11 and MySQL 8.0 (real servers, full conformance suite) and Dolt
MongoDBmongodb@sethucms/adapter-mongodbmongodb (peer)FerretDB 1.24 (MongoDB-compatible), not MongoDB itself
Firestorefirestore@sethucms/adapter-firestore@google-cloud/firestore (peer)an in-memory fake, not Firestore

Read the "Tested against" column before you rely on an adapter. A compatible server is not the real thing: MySQL, MariaDB, MongoDB and Firestore have not been run through these tests, and you should run the live tests (below) against your own server before production use.

Peer drivers are not installed for you. Add the one you need to the gateway (pnpm add mysql2 in apps/api).

Shared rules

  • Passwords and keys go in the connection's credentials, which the vault encrypts. They never appear in settings, logs or error messages.
  • Network connections go through the gateway's network guard (private ranges blocked in production unless SETHUCMS_ALLOW_PRIVATE_NETWORKS=true).
  • Connections can be read-only. Writes are then refused before reaching the driver.
  • Every adapter can create a missing collection when a migration asks for it (doc 17).

SQLite

For small sites, edge deployments and local work. A "connection" is a file on the gateway's disk.

SettingMeaning
path:memory: or a file name inside the folder set by SETHUCMS_SQLITE_DIR
busyTimeoutMshow long to wait for a lock (0-60000, default 5000)
  • File databases are off until SETHUCMS_SQLITE_DIR is set. Paths with .., absolute paths outside the folder, and URIs are refused.
  • Needs Node 22.5 or newer (node:sqlite). The Docker image must use Node 22.
  • SQLite allows one writer at a time. Do not point another program at a file the gateway is writing unless you accept lock waits.
  • Back up with the SQLite backup API or by copying the file while the gateway is stopped. Copying a live file can give a torn copy.

MySQL and MariaDB

SettingMeaning
host, portserver address (port default 3306)
databasedatabase name
ssldefault true; turn off only on a trusted private link
maxConnections1-20, default 5
statementTimeoutMs100-120000, default 15000
credentials user, passwordencrypted in the vault
  • Identifiers are quoted with backticks and values are always parameters.
  • Pagination is offset based.
  • LIKE wildcards in user text are escaped.
  • Not verified on MySQL 5.7 or 9, or other MariaDB versions. MariaDB 10.11 and MySQL 8.0 are verified. It has no real JSON type (JSON is LONGTEXT plus a json_valid check), and the adapter finds those columns from the check; a bug where they came back as text was found and fixed that way.

MongoDB

MongoDB has no schema, so the adapter works out collection shapes by sampling documents, or you declare them.

SettingMeaning
host, port, srvaddress; srv: true for Atlas mongodb+srv (no port)
database, authSourcedatabase, and where the user is defined (default admin)
tlsdefault true
sampleSizedocuments sampled per collection (1-1000, default 100)
collectionsdeclared definitions that replace sampling
maxConnections, statementTimeoutMsas above
  • Integer ids are issued from a counter collection _sethucms_counters; declared and discovered shapes are kept in _sethucms_schemas. Both are hidden from content.
  • Queries are built as typed documents from the allowlisted filter tree. Operators in user values ($ne, $where) are rejected before they reach the driver.
  • Sampling can miss rare fields. Declare the collections you care about.
  • Tested against FerretDB 1.24 only. Real MongoDB behaviour (collation, index use, transactions) is not verified.

Firestore

SettingMeaning
projectId, databaseIdproject and database (default (default))
emulatorHosthost:port of the emulator, for development only
sampleSize, collectionsas for MongoDB
maxScanDocumentsmost documents a text search may read (10-50000, default 5000)
credentials serviceAccountservice account key JSON, encrypted in the vault; otherwise Google default credentials
  • Firestore cannot do OR with inequality, several inequality fields, or arbitrary text search. The gateway refuses such queries (CAPABILITY_UNSUPPORTED) instead of approximating them.
  • contains and startsWith on text are done by reading up to maxScanDocuments documents; they are slow and bounded on purpose.
  • Integer ids come from _sethucms_counters, as for MongoDB.
  • A new, empty collection. Firestore has a collection only once it holds a document, so a scan of an empty database finds nothing. An administrator can declare one: on the connection page, fill in New collection and Fields, or call POST /v1/admin/connections/{id}/collections with { "name": "projects", "fields": [{ "name": "title", "type": "string", "required": true }] }. Only the definition is saved (in _sethucms_schemas); the database makes the collection with its first record. The connection must be read and write, and the collection is switched on unless you send "expose": false. The saved fields are the ones records may use, and they cannot be edited yet. Choose them with the data in mind. A collection made by hand in the Firebase console is different: its fields are guessed from the documents already in it. This is offered only for Firestore. It is not offered for SQL databases, where creating tables stays a migration step (doc 17).
  • Files. A Firestore connection can also store images and PDFs, with cleaning, a type check on the bytes and limits (doc 31).
  • The content adapter was only tested against an in-memory fake that we wrote. (The gateway's shared state store, a different piece of code, is also tested against the Firestore emulator; see doc 26.) The adapter has never talked to Firestore or the emulator. Treat this adapter as experimental until you have run it against the emulator or a test project.

Running the live tests against your own server

From sethucms-adapters:

SETHUCMS_TEST_PG_URL=postgres://user:pass@host:5432/db  pnpm --filter @sethucms/adapter-postgres test
SETHUCMS_TEST_MYSQL_URL=mysql://user:pass@host:3306     pnpm --filter @sethucms/adapter-mysql test
SETHUCMS_TEST_MONGO_URL=mongodb://host:27017            pnpm --filter @sethucms/adapter-mongodb test

The tests create and drop their own scratch database or collections. Use a throwaway server. Without these variables the live tests are skipped.

Writing your own adapter

Implement the Adapter contract (doc 5) and run defineConformanceSuite. SQL databases should build on @sethucms/adapter-sql-kit, which holds the dialects, query builders, table creation and row normalising shared by PostgreSQL, MySQL and SQLite. Optional methods: createCollection (needed for migration table creation) and finishBulkLoad (for example, moving a sequence past copied ids).