Multi-Database Support
FraiseQL supports multiple database backends, allowing you to use your preferred database while maintaining the same GraphQL API.
Supported Databases
Section titled “Supported Databases”| Database | Status | Best For |
|---|---|---|
| PostgreSQL | Primary | Production, full feature set |
| MySQL/MariaDB | Supported | MySQL-based infrastructure |
| SQLite | Supported | Development, embedded, testing |
| SQL Server | Supported | Enterprise Windows environments |
Configuration
Section titled “Configuration”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.
PostgreSQL (Default)
Section titled “PostgreSQL (Default)”[database]type = "postgresql"url = "postgresql://user:password@localhost:5432/mydb"pool_min = 5pool_max = 20idle_timeout_ms = 300000connect_timeout_ms = 5000ssl_mode = "prefer"MySQL / MariaDB
Section titled “MySQL / MariaDB”[database]type = "mysql"url = "mysql://user:password@localhost:3306/mydb"pool_min = 5pool_max = 20SQLite
Section titled “SQLite”[database]type = "sqlite"url = "sqlite:///path/to/database.db"
# Or in-memory for testing# url = "sqlite::memory:"SQL Server
Section titled “SQL Server”[database]type = "sqlserver"url = "mssql://user:password@localhost:1433/mydb"Feature Matrix
Section titled “Feature Matrix”| Feature | PostgreSQL | MySQL | SQLite | SQL Server |
|---|---|---|---|---|
| JSONB columns | Native | JSON type | JSON1 ext | NVARCHAR |
| UUID type | Native | CHAR(36) | TEXT | UNIQUEIDENTIFIER |
| Arrays | Native | JSON | JSON | JSON |
| Full-text search | Native | FULLTEXT | FTS5 | Full-text |
| Window functions | Full | Full | Limited | Full |
| CTEs | Recursive | Recursive | Recursive | Recursive |
| Subscriptions | LISTEN/NOTIFY | Polling | Polling | Polling |
| Connection pooling | Deadpool | Deadpool | Single | Deadpool |
Database-Specific Considerations
Section titled “Database-Specific Considerations”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 JSONBCREATE INDEX idx_user_data ON tb_user USING GIN (data);
-- Native array supportCREATE 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
JSON stored as JSON type:
-- MySQL adaptation of the trinity pattern-- Note: MySQL 8.0+ supports GENERATED ALWAYS AS IDENTITY syntaxCREATE TABLE tb_user ( pk_user BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, id CHAR(36) NOT NULL UNIQUE DEFAULT (UUID()), identifier VARCHAR(255) NOT NULL UNIQUE, data JSON NOT NULL);
-- Generated column for indexingALTER TABLE tb_userADD COLUMN email VARCHAR(255) AS (JSON_UNQUOTE(data->>'$.email')) STORED,ADD INDEX idx_user_email (email);Notes:
- Use
JSON_UNQUOTE()for string extraction - Generated columns for indexed access to JSON fields
- UUID stored as CHAR(36)
- JSON functions:
JSON_OBJECT(),JSON_ARRAYAGG()
Lightweight, ideal for development:
-- SQLite adaptation of the trinity patternCREATE TABLE tb_user ( pk_user INTEGER PRIMARY KEY AUTOINCREMENT, id TEXT NOT NULL UNIQUE DEFAULT (lower(hex(randomblob(16)))), identifier TEXT NOT NULL UNIQUE, data TEXT NOT NULL CHECK (json_valid(data)));
-- Expression index for JSONCREATE INDEX idx_user_email ON tb_user (json_extract(data, '$.email'));Notes:
- Enable JSON1 extension
- UUID generated as hex string
- Single writer, multiple readers (WAL mode)
- JSON functions:
json_object(),json_group_array()
Enterprise Windows support:
-- SQL Server adaptation of the trinity patternCREATE TABLE dbo.tb_user ( pk_user BIGINT IDENTITY(1,1) PRIMARY KEY, id UNIQUEIDENTIFIER NOT NULL UNIQUE DEFAULT NEWID(), identifier NVARCHAR(255) NOT NULL UNIQUE, data NVARCHAR(MAX) NOT NULL);
-- Computed column for indexingALTER TABLE dbo.tb_userADD email AS JSON_VALUE(data, '$.email');CREATE INDEX idx_user_email ON dbo.tb_user (email);Notes:
- JSON stored in NVARCHAR(MAX)
- Use
JSON_VALUE()andJSON_QUERY() - UNIQUEIDENTIFIER for UUIDs
- Use
FOR JSON PATHfor building JSON
Schema Adaptation
Section titled “Schema Adaptation”FraiseQL automatically adapts SQL generation per database:
WHERE Clause Generation
Section titled “WHERE Clause Generation”query { users(where: { email: { _eq: "user@example.com" } }) { id name }}SELECT * FROM v_user WHERE data->>'email' = 'user@example.com'SELECT * FROM v_user WHERE JSON_UNQUOTE(data->'$.email') = 'user@example.com'SELECT * FROM v_user WHERE json_extract(data, '$.email') = 'user@example.com'SELECT * FROM v_user WHERE JSON_VALUE(data, '$.email') = 'user@example.com'Multi-Database Architecture
Section titled “Multi-Database Architecture”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.
Development Workflow
Section titled “Development Workflow”Local Development (SQLite)
Section titled “Local Development (SQLite)”[database]type = "sqlite"url = "sqlite:///dev.db"# Quick iterationfraiseql run --config dev.tomlTesting (SQLite In-Memory)
Section titled “Testing (SQLite In-Memory)”[database]type = "sqlite"url = "sqlite::memory:"Production (PostgreSQL)
Section titled “Production (PostgreSQL)”[database]type = "postgresql"url = "${DATABASE_URL}"pool_min = 10pool_max = 50idle_timeout_ms = 600000connect_timeout_ms = 5000ssl_mode = "require"Connection Pooling
Section titled “Connection Pooling”Pool Configuration
Section titled “Pool Configuration”Pool settings are direct fields on the [database] section:
[database]url = "postgresql://user:password@localhost:5432/mydb"pool_min = 5pool_max = 20connect_timeout_ms = 30000idle_timeout_ms = 600000| Key | Default | Description |
|---|---|---|
pool_min | 2 | Minimum connections to keep open |
pool_max | 20 | Maximum connections in the pool |
connect_timeout_ms | 5000 | Milliseconds to wait for a connection |
idle_timeout_ms | 600000 | Milliseconds before idle connections are closed |
SQLite
Section titled “SQLite”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"Troubleshooting
Section titled “Troubleshooting”Connection Issues
Section titled “Connection Issues”Check connection by verifying your URL is reachable from the host running FraiseQL:
# PostgreSQLpsql "${DATABASE_URL}" -c "SELECT 1"
# MySQLmysql -u user -p -h localhost mydb -e "SELECT 1"Performance Problems
Section titled “Performance Problems”- Check pool settings —
pool_maxtoo low causes queuing under load - Verify indexes on JSON fields
- Review query plans with
fraiseql-cli explain
Transport Support Across Databases
Section titled “Transport Support Across Databases”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:
| Database | JSON views (GraphQL + REST) | Row views (gRPC) |
|---|---|---|
| PostgreSQL | json_agg(row_to_json(t)) | Standard SELECT |
| MySQL | JSON_ARRAYAGG(...) | Standard SELECT |
| SQL Server | FOR JSON PATH | Standard SELECT |
| SQLite | json_group_array(...) | Standard SELECT |
Transport choice is independent of database choice.
Next Steps
Section titled “Next Steps”- Schema Design - Database-agnostic patterns
- Performance - Database optimization
- Deployment - Production database setup
- Multi-Tenancy - Tenant isolation with PostgreSQL RLS