Azure Deployment
Provision Azure SQL, Container Apps, and Key Vault end-to-end. Azure Guide
Most GraphQL frameworks were designed for PostgreSQL-first teams. FraiseQL is different — SQL Server is a first-class runtime target, not an afterthought. If your organization already runs SQL Server for compliance, licensing, or organizational standards, FraiseQL compiles your schema to T-SQL and runs natively without requiring a database migration.
This guide covers enterprise-specific patterns: Azure AD authentication, Always On high availability, Transparent Data Encryption, Row-Level Security, and compliance frameworks.
For on-premises SQL Server with Active Directory:
[database]url = "server=SQLSERVER01;database=fraiseql_db;integratedSecurity=true;trustServerCertificate=true"Use a dedicated service account rather than a developer’s AD account for production:
# Create service account in Active DirectoryNew-ADUser -Name "fraiseql-svc" ` -SamAccountName "fraiseql-svc" ` -AccountPassword (Read-Host -AsSecureString "Password") ` -Enabled $true
# Grant SQL Server loginInvoke-Sqlcmd -Query " CREATE LOGIN [DOMAIN\fraiseql-svc] FROM WINDOWS; USE fraiseql_db; CREATE USER [DOMAIN\fraiseql-svc] FOR LOGIN [DOMAIN\fraiseql-svc]; ALTER ROLE db_datareader ADD MEMBER [DOMAIN\fraiseql-svc]; ALTER ROLE db_datawriter ADD MEMBER [DOMAIN\fraiseql-svc];"For Azure deployments, Managed Identity eliminates stored credentials entirely:
[database]url = "server=fraiseql-sql.database.windows.net;database=fraiseql_db;Authentication=ActiveDirectoryMsi"Setup:
Enable system-assigned managed identity on your compute resource:
# Azure Container Appsaz containerapp identity assign --name fraiseql-app \ --resource-group fraiseql-rg --system-assigned
# Azure App Serviceaz webapp identity assign --name fraiseql-app \ --resource-group fraiseql-rgSet an Azure AD admin on the SQL server (required before creating AD users):
az sql server ad-admin create \ --resource-group fraiseql-rg \ --server fraiseql-sql \ --display-name "Your AD Admin Name" \ --object-id <your-aad-user-object-id>Create a SQL login for the managed identity (connect as an AD admin):
-- Run against the target database, not masterCREATE USER [fraiseql-app] FROM EXTERNAL PROVIDER;ALTER ROLE db_datareader ADD MEMBER [fraiseql-app];ALTER ROLE db_datawriter ADD MEMBER [fraiseql-app];With this configuration, credentials are rotated automatically by Azure. No passwords in environment variables, config files, or deployment pipelines.
For automated deployments or environments where Managed Identity is not available:
[database]url = "server=<server>.database.windows.net;database=<db>;Authentication=ActiveDirectoryServicePrincipal;User Id=<app-id>@<tenant-id>;Password=<client-secret>"Store the client secret in Azure Key Vault and reference it via an environment variable.
FraiseQL’s connection pool connects to the AG listener URL. SQL Server’s AG listener handles
routing to the primary for writes and can route to a secondary for read-only queries via
ApplicationIntent=ReadOnly:
[database]# Primary — used for mutationsurl = "server=fraiseql-ag-listener.corp.local,1433;database=fraiseql_db;ApplicationIntent=ReadWrite;MultiSubnetFailover=True"FraiseQL’s connection pool uses SQL Server’s native reconnection behavior. On a failover:
For applications requiring sub-second failover, configure a higher pool timeout and implement retry logic in your GraphQL client.
FraiseQL’s inject parameter sets SESSION_CONTEXT from authenticated JWT claims. SQL
Server’s native RLS uses that context to filter rows — no application-layer filtering needed:
-- Step 1: Create RLS functionCREATE FUNCTION rls.fn_user_filter(@user_id NVARCHAR(36))RETURNS TABLE WITH SCHEMABINDING ASRETURN SELECT 1 AS fn_resultWHERE JSON_VALUE(data, '$.user_id') = @user_id OR IS_ROLEMEMBER('db_admin') = 1; -- bypass for admin role
-- Step 2: Create security policy on the viewCREATE SECURITY POLICY dbo.UserDataPolicy ADD FILTER PREDICATE rls.fn_user_filter( CAST(SESSION_CONTEXT(N'user_id') AS NVARCHAR(36)) ) ON dbo.v_documentsWITH (STATE = ON);FraiseQL sets SESSION_CONTEXT automatically when you declare inject on a query:
@fraiseql.querydef documents(limit: int = 20) -> list[Document]: return fraiseql.config( sql_source="v_documents", inject={"user_id": "sub"}, # inject JWT 'sub' claim as SESSION_CONTEXT 'user_id' )This provides defense in depth: FraiseQL filters at the application layer; SQL Server RLS filters at the database layer. A bug in one layer does not expose data.
TDE encrypts the database at rest at the storage layer. FraiseQL works with TDE without any application changes:
-- Enable TDE on SQL Server 2016+ (on-premises)USE master;CREATE MASTER KEY ENCRYPTION BY PASSWORD = '<strong-password>';CREATE CERTIFICATE TDECert WITH SUBJECT = 'FraiseQL TDE Certificate';
USE fraiseql_db;CREATE DATABASE ENCRYPTION KEY WITH ALGORITHM = AES_256 ENCRYPTION BY SERVER CERTIFICATE TDECert;ALTER DATABASE fraiseql_db SET ENCRYPTION ON;
-- Verify encryption statusSELECT db.name, dek.encryption_state_desc, dek.percent_completeFROM sys.dm_database_encryption_keys dekJOIN sys.databases db ON dek.database_id = db.database_id;On Azure SQL Database, TDE is enabled by default on all databases. No action required.
SQL Server has two native audit mechanisms that complement FraiseQL’s application-level logs:
-- Create server audit (writes to file or Windows event log)CREATE SERVER AUDIT fraiseql_audit TO FILE (FILEPATH = 'C:\Audit\', MAXSIZE = 100 MB);ALTER SERVER AUDIT fraiseql_audit WITH (STATE = ON);
-- Create database audit specificationUSE fraiseql_db;CREATE DATABASE AUDIT SPECIFICATION fraiseql_db_audit FOR SERVER AUDIT fraiseql_audit ADD (SELECT, INSERT, UPDATE, DELETE ON SCHEMA::dbo BY PUBLIC)WITH (STATE = ON);For row-level change tracking (useful for event sourcing or audit trails):
-- Enable CDC on the databaseEXEC sys.sp_cdc_enable_db;
-- Enable CDC on a specific tableEXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 'tb_orders', @role_name = NULL;
-- Query change historySELECT * FROM cdc.dbo_tb_orders_CTWHERE __$start_lsn > @last_lsnORDER BY __$start_lsn;Key requirements and how FraiseQL + SQL Server address them:
| Requirement | Implementation |
|---|---|
| Access controls | SQL Server RLS + FraiseQL inject (user-scoped queries) |
| Audit trails | SQL Server Audit + FraiseQL [security] audit logging |
| Encryption at rest | TDE on all database files |
| Encryption in transit | encrypt=true in connection string (enforced on Azure SQL) |
| Minimum necessary | Row-level and column-level access via SQL views — only expose what the view includes |
| Control | Implementation |
|---|---|
| CC6.1 — Logical access controls | SQL Server logins + RLS + Windows/Azure AD auth |
| CC6.3 — Role-based access | SQL Server roles (db_datareader, db_datawriter, custom roles) |
| CC7.2 — Monitoring | SQL Server Audit + Application Insights + FraiseQL observer logs |
| CC8.1 — Change management | FraiseQL schema compilation is version-controlled; schema changes produce a new compiled artifact |
| A1.2 — Capacity planning | SQL Server DMVs + Azure Monitor metrics |
PCI-DSS requires that cardholder data (card numbers, CVV, expiry) is never stored in plaintext. SQL Server’s Always Encrypted feature encrypts data at the column level, such that the database server itself never sees plaintext values:
-- Enable Always Encrypted on a columnALTER TABLE tb_paymentALTER COLUMN card_number NVARCHAR(20) ENCRYPTED WITH ( COLUMN_ENCRYPTION_KEY = CardDataKey, ENCRYPTION_TYPE = DETERMINISTIC, -- allows equality search ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256' ) NOT NULL;| Tier | Best for | Auto-pause | Max vCores |
|---|---|---|---|
| Serverless | Development, staging | Configurable (saves cost) | 40 |
| General Purpose | Most production workloads | No | 128 |
| Business Critical | High IOPS, local SSD | No | 128 |
| Hyperscale | Large databases (>4TB) | No | 128 |
Recommended fraiseql.toml pool settings by tier:
[database]url = "${DATABASE_URL}"pool_min = 1pool_max = 5connect_timeout_ms = 60000 # allow time for serverless resume[database]url = "${DATABASE_URL}"pool_min = 2pool_max = 20connect_timeout_ms = 5000idle_timeout_ms = 600000ssl_mode = "require"[database]url = "${DATABASE_URL}"pool_min = 5pool_max = 50connect_timeout_ms = 3000idle_timeout_ms = 300000ssl_mode = "require"Azure Deployment
Provision Azure SQL, Container Apps, and Key Vault end-to-end. Azure Guide
SQL Server Setup
Connection strings, schema patterns, and SQL Server-specific examples. SQL Server Guide
SQL Server Troubleshooting
JSON indexes, Always On lag, Azure firewall, and AD auth failures. Troubleshooting
Server-Side Injection
How FraiseQL injects JWT claims for RLS and multi-tenancy. Injection Guide