GraphQL API
Fluxbase provides an auto-generated GraphQL API that exposes your PostgreSQL tables as a fully typed GraphQL schema. The schema is automatically generated from your database tables, including relationships, filters, and mutations.
Overview
Section titled “Overview”The GraphQL API provides:
- Auto-generated types from your PostgreSQL tables
- Query support with filtering, ordering, and pagination
- Mutation support for insert, update, and delete operations
- Nested queries following foreign key relationships
- Row Level Security (RLS) enforcement on all operations
- Introspection for schema discovery (configurable)
Endpoint
Section titled “Endpoint”POST /api/v1/graphqlAuthentication
Section titled “Authentication”The GraphQL endpoint uses the same authentication as the REST API. Include a JWT token in the Authorization header:
curl -X POST http://localhost:8080/api/v1/graphql \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"query": "{ users { id email } }"}'Configuration
Section titled “Configuration”Configure GraphQL in your fluxbase.yaml or via environment variables:
graphql: enabled: true max_depth: 10 max_complexity: 1000 introspection: true allow_fragments: false max_fields_per_lvl: 50| Setting | Env Variable | Default | Description |
|---|---|---|---|
enabled | FLUXBASE_GRAPHQL_ENABLED | true | Enable/disable GraphQL endpoint |
max_depth | FLUXBASE_GRAPHQL_MAX_DEPTH | 10 | Maximum query nesting depth |
max_complexity | FLUXBASE_GRAPHQL_MAX_COMPLEXITY | 1000 | Maximum query complexity score |
introspection | FLUXBASE_GRAPHQL_INTROSPECTION | true | Allow schema introspection |
allow_fragments | FLUXBASE_GRAPHQL_ALLOW_FRAGMENTS | false | Allow GraphQL fragments |
max_fields_per_lvl | FLUXBASE_GRAPHQL_MAX_FIELDS_PER_LVL | 50 | Maximum fields per nesting level |
Query Syntax
Section titled “Query Syntax”Basic Query
Section titled “Basic Query”query { users { id email name createdAt }}With Filtering
Section titled “With Filtering”query { users(filter: { email_eq: "john@example.com" }) { id email }}With Ordering and Pagination
Section titled “With Ordering and Pagination”query { users( orderBy: [{ createdAt: DESC }] limit: 10 offset: 0 ) { id email createdAt }}Nested Queries (Relationships)
Section titled “Nested Queries (Relationships)”query { users { id email posts { id title comments { id content author { email } } } }}Filter Operators
Section titled “Filter Operators”The GraphQL API supports PostgREST-compatible filter operators, specified as flat fields in the filter argument:
| Operator | Description | Example |
|---|---|---|
_eq | Equal | { status_eq: "active" } |
_neq | Not equal | { status_neq: "deleted" } |
_gt | Greater than | { age_gt: 18 } |
_gte | Greater than or equal | { age_gte: 18 } |
_lt | Less than | { price_lt: 100 } |
_lte | Less than or equal | { price_lte: 100 } |
_like | Pattern match | { name_like: "John%" } |
_ilike | Case-insensitive match | { name_ilike: "john%" } |
_in | In list | { status_in: ["active", "pending"] } |
_is_null | Is null | { deletedAt_is_null: true } |
Multiple conditions in a single filter object are combined with AND.
Mutations
Section titled “Mutations”Insert
Section titled “Insert”mutation { insertUser(data: { email: "new@example.com", name: "New User" }) { id email }}Update
Section titled “Update”mutation { updateUser(id: "user-uuid", data: { name: "Updated Name" }) { id name }}Bulk Update
Section titled “Bulk Update”mutation { updateManyUser(filter: { status_eq: "active" }, data: { status: "archived" })}updateMany returns an Int count of affected rows.
Delete
Section titled “Delete”mutation { deleteUser(id: "user-uuid") { id name }}Bulk Delete
Section titled “Bulk Delete”mutation { deleteManyUser(filter: { status_eq: "inactive" })}deleteMany returns an Int count of deleted rows.
Type Mapping
Section titled “Type Mapping”PostgreSQL types are automatically mapped to GraphQL types:
| PostgreSQL | GraphQL |
|---|---|
text, varchar, char | String |
integer, smallint | Int |
bigint | BigIntScalar (custom) |
boolean | Boolean |
numeric, real, double precision | Float |
uuid | UUIDScalar (custom) |
json, jsonb | JSON (custom scalar) |
timestamp, timestamptz | DateTime (custom scalar) |
date | Date (custom scalar) |
array types | JSONScalar |
Introspection
Section titled “Introspection”When enabled, you can query the schema:
query { __schema { types { name fields { name type { name } } } }}Or get type details:
query { __type(name: "users") { name fields { name type { name kind } } }}CLI Usage
Section titled “CLI Usage”The Fluxbase CLI provides a graphql command for executing queries and mutations from the command line.
# Execute a queryfluxbase graphql query '{ users { id email } }'
# Query with variablesfluxbase graphql query 'query($id: ID!) { user(id: $id) { email } }' --var 'id=123'
# Execute from filefluxbase graphql query --file ./query.graphql
# Execute a mutationfluxbase graphql mutation 'mutation { insertUser(data: {email: "new@example.com"}) { id } }'
# Introspect the schemafluxbase graphql introspect
# List types onlyfluxbase graphql introspect --typesSee the CLI Command Reference for complete documentation.
SDK Usage
Section titled “SDK Usage”TypeScript SDK
Section titled “TypeScript SDK”import { createClient } from '@nimbleflux/fluxbase-sdk'
const client = createClient('http://localhost:8080', 'your-anon-key')
// Execute a queryconst { data, errors } = await client.graphql.query<UsersQuery>(` query GetUsers($limit: Int) { users(limit: $limit) { id email } }`, { limit: 10 })
// Execute a mutationconst { data, errors } = await client.graphql.mutation<CreateUserMutation>(` mutation CreateUser($data: UserInput!) { insertUser(data: $data) { id email } }`, { data: { email: 'new@example.com' } })React SDK
Section titled “React SDK”import { useGraphQLQuery, useGraphQLMutation } from '@nimbleflux/fluxbase-sdk-react'
function UsersList() { const { data, isLoading, error } = useGraphQLQuery<UsersQuery>( 'users-list', `query { users { id email } }` )
if (isLoading) return <div>Loading...</div> if (error) return <div>Error: {error.message}</div>
return ( <ul> {data?.users.map(user => ( <li key={user.id}>{user.email}</li> ))} </ul> )}
function CreateUserForm() { const mutation = useGraphQLMutation<CreateUserMutation>( `mutation CreateUser($data: UserInput!) { insertUser(data: $data) { id email } }`, { onSuccess: (data) => console.log('Created:', data), invalidateQueries: ['users-list'] } )
const handleSubmit = (email: string) => { mutation.mutate({ data: { email } }) }
return ( <button onClick={() => handleSubmit('new@example.com')}> Create User </button> )}Admin Dashboard
Section titled “Admin Dashboard”The Query Editor in the Admin Dashboard supports both SQL and GraphQL modes:
- Navigate to Query Editor in the sidebar
- Click the GraphQL tab at the top of the editor
- Write your GraphQL query with auto-completion support
- Press Ctrl+Enter (or Cmd+Enter on Mac) to execute
The editor provides:
- Syntax highlighting for GraphQL
- Auto-completion for types, fields, and operations
- Query history tracking for both SQL and GraphQL queries
- JSON result formatting with error display
Row Level Security
Section titled “Row Level Security”The GraphQL API enforces Row Level Security (RLS) policies exactly like the REST API:
- Anonymous users execute queries as the
anonPostgreSQL role - Authenticated users execute as the
authenticatedrole - Service role keys bypass RLS for admin operations
- Tenant service keys execute as the
tenant_servicerole (tenant-scoped viaapp.current_tenant_id)
Session variables are available in your RLS policies:
CREATE POLICY "Users can view own data" ON users FOR SELECT USING (id = (current_setting('request.jwt.claims', true)::json->>'sub')::uuid);Multi-Tenancy in GraphQL
Section titled “Multi-Tenancy in GraphQL”For multi-tenant deployments, use the X-FB-Tenant header to specify the tenant context:
curl -X POST http://localhost:8080/api/v1/graphql \ -H "Authorization: Bearer <service-key>" \ -H "X-FB-Tenant: acme-corp" \ -H "Content-Type: application/json" \ -d '{"query": "{ posts { id title } }"}'When tenant context is set:
- Queries are routed to the tenant’s database (or main database for the default tenant)
- RLS policies using
app.current_tenant_idautomatically filter data - The
tenant_admindashboard role maps toauthenticated(respects RLS) - The
tenant_servicerole enforces RLS with tenant context - The
instance_adminrole maps toservice_role(bypasses RLS)
Security Best Practices
Section titled “Security Best Practices”Production Configuration
Section titled “Production Configuration”graphql: enabled: true max_depth: 5 max_complexity: 500 introspection: false allow_fragments: false max_fields_per_lvl: 50Query Depth Limiting
Section titled “Query Depth Limiting”The max_depth setting prevents deeply nested queries that could be expensive:
# This query has depth 4 (users -> posts -> comments -> author)query { users { # depth 1 posts { # depth 2 comments { # depth 3 author { # depth 4 email } } } }}Complexity Limiting
Section titled “Complexity Limiting”Query complexity is calculated based on the number of fields and nesting. The max_complexity setting prevents resource-intensive queries.
Error Handling
Section titled “Error Handling”GraphQL errors are returned in the standard GraphQL error format:
{ "data": null, "errors": [ { "message": "permission denied for table users", "locations": [{ "line": 2, "column": 3 }], "path": ["users"] } ]}Common error types:
- Validation errors: Invalid query syntax or unknown fields
- Authorization errors: RLS policy violations
- Depth/complexity errors: Query exceeds configured limits
Comparison with REST API
Section titled “Comparison with REST API”| Feature | GraphQL | REST |
|---|---|---|
| Request format | POST with query body | GET/POST/PUT/DELETE |
| Response shape | Exactly what you request | Fixed per endpoint |
| Nested data | Single request | Multiple requests |
| Caching | Requires client setup | HTTP caching |
| Learning curve | Higher | Lower |
Choose GraphQL when you need:
- Complex nested queries
- Flexible response shapes
- Strong typing with introspection
Choose REST when you need:
- Simple CRUD operations
- HTTP caching
- Simpler integration
Troubleshooting
Section titled “Troubleshooting”GraphQL endpoint returns 404
Section titled “GraphQL endpoint returns 404”Ensure GraphQL is enabled in your configuration:
export FLUXBASE_GRAPHQL_ENABLED=trueQuery depth exceeded
Section titled “Query depth exceeded”Reduce the nesting in your query or increase max_depth:
graphql: max_depth: 15Permission denied errors
Section titled “Permission denied errors”Check that:
- Your JWT token is valid and not expired
- RLS policies allow the operation
- You’re using the correct role (anon vs authenticated)
Introspection not working
Section titled “Introspection not working”Enable introspection (note: disable in production):
graphql: introspection: true