Skip to content

Multi-Database Support

FraiseQL supports multiple database backends, allowing you to use your preferred database while maintaining the same GraphQL API.

DatabaseStatusBest For
PostgreSQLPrimaryProduction, full feature set
MySQL/MariaDBSupportedMySQL-based infrastructure
SQLiteSupportedDevelopment, embedded, testing
SQL ServerSupportedEnterprise Windows environments

Database configuration is set in the [database] section of fraiseql.toml. Pool settings are direct fields on [database] — there is no [database.pool] sub-table.

[database]
type = "postgresql"
url = "postgresql://user:password@localhost:5432/mydb"
pool_min = 5
pool_max = 20
idle_timeout_ms = 300000
connect_timeout_ms = 5000
ssl_mode = "prefer"
[database]
type = "mysql"
url = "mysql://user:password@localhost:3306/mydb"
pool_min = 5
pool_max = 20
[database]
type = "sqlite"
url = "sqlite:///path/to/database.db"
# Or in-memory for testing
# url = "sqlite::memory:"
[database]
type = "sqlserver"
url = "mssql://user:password@localhost:1433/mydb"
FeaturePostgreSQLMySQLSQLiteSQL Server
JSONB columnsNativeJSON typeJSON1 extNVARCHAR
UUID typeNativeCHAR(36)TEXTUNIQUEIDENTIFIER
ArraysNativeJSONJSONJSON
Full-text searchNativeFULLTEXTFTS5Full-text
Window functionsFullFullLimitedFull
CTEsRecursiveRecursiveRecursiveRecursive
SubscriptionsLISTEN/NOTIFYPollingPollingPolling
Connection poolingDeadpoolDeadpoolSingleDeadpool

Full feature support, recommended for production:

-- Trinity pattern: pk_ (internal), id (public UUID), identifier (human key)
CREATE TABLE tb_user (
pk_user BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
id UUID DEFAULT gen_random_uuid() UNIQUE NOT NULL,
identifier TEXT UNIQUE NOT NULL,
data JSONB NOT NULL
);
-- GIN index for JSONB
CREATE INDEX idx_user_data ON tb_user USING GIN (data);
-- Native array support
CREATE TABLE tb_post (
pk_post BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
id UUID DEFAULT gen_random_uuid() UNIQUE NOT NULL,
identifier TEXT UNIQUE NOT NULL,
tags TEXT[] NOT NULL
);

Advantages:

  • Native JSONB type with full indexing support
  • GIN indexes for efficient JSON queries
  • Native array types
  • LISTEN/NOTIFY for subscriptions

FraiseQL automatically adapts SQL generation per database:

query {
users(where: { email: { _eq: "user@example.com" } }) {
id
name
}
}
SELECT * FROM v_user WHERE data->>'email' = 'user@example.com'

FraiseQL does not support connecting a single service to multiple databases simultaneously. To expose multiple databases through a unified GraphQL API, run a separate FraiseQL service for each database and compose them with Apollo Federation:

Client
└── Apollo Federation Gateway
├── fraiseql-users (PostgreSQL)
├── fraiseql-orders (PostgreSQL — separate cluster)
└── fraiseql-legacy (SQL Server)

Each service has its own fraiseql.toml with its own [database] section and schema. The Federation gateway merges the subgraph schemas into a single API surface.

dev.toml
[database]
type = "sqlite"
url = "sqlite:///dev.db"
Terminal window
# Quick iteration
fraiseql run --config dev.toml
test.toml
[database]
type = "sqlite"
url = "sqlite::memory:"
prod.toml
[database]
type = "postgresql"
url = "${DATABASE_URL}"
pool_min = 10
pool_max = 50
idle_timeout_ms = 600000
connect_timeout_ms = 5000
ssl_mode = "require"

Pool settings are direct fields on the [database] section:

[database]
url = "postgresql://user:password@localhost:5432/mydb"
pool_min = 5
pool_max = 20
connect_timeout_ms = 30000
idle_timeout_ms = 600000
KeyDefaultDescription
pool_min2Minimum connections to keep open
pool_max20Maximum connections in the pool
connect_timeout_ms5000Milliseconds to wait for a connection
idle_timeout_ms600000Milliseconds before idle connections are closed

SQLite uses a single connection with WAL mode. Configure WAL and busy timeout via the connection URL:

[database]
type = "sqlite"
url = "sqlite:///path/to/db.sqlite?mode=rwc&journal_mode=wal&busy_timeout=5000"

Check connection by verifying your URL is reachable from the host running FraiseQL:

Terminal window
# PostgreSQL
psql "${DATABASE_URL}" -c "SELECT 1"
# MySQL
mysql -u user -p -h localhost mydb -e "SELECT 1"
  1. Check pool settings — pool_max too low causes queuing under load
  2. Verify indexes on JSON fields
  3. Review query plans with fraiseql-cli explain

All four databases (PostgreSQL, MySQL, SQL Server, SQLite) support all three transports — GraphQL, REST, and gRPC. The compiler generates transport-aware views for each database dialect:

DatabaseJSON views (GraphQL + REST)Row views (gRPC)
PostgreSQLjson_agg(row_to_json(t))Standard SELECT
MySQLJSON_ARRAYAGG(...)Standard SELECT
SQL ServerFOR JSON PATHStandard SELECT
SQLitejson_group_array(...)Standard SELECT

Transport choice is independent of database choice.