Skip to content

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.

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)
POST /api/v1/graphql

The GraphQL endpoint uses the same authentication as the REST API. Include a JWT token in the Authorization header:

Terminal window
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 } }"}'

Configure GraphQL in your fluxbase.yaml or via environment variables:

fluxbase.yaml
graphql:
enabled: true
max_depth: 10
max_complexity: 1000
introspection: true
allow_fragments: false
max_fields_per_lvl: 50
SettingEnv VariableDefaultDescription
enabledFLUXBASE_GRAPHQL_ENABLEDtrueEnable/disable GraphQL endpoint
max_depthFLUXBASE_GRAPHQL_MAX_DEPTH10Maximum query nesting depth
max_complexityFLUXBASE_GRAPHQL_MAX_COMPLEXITY1000Maximum query complexity score
introspectionFLUXBASE_GRAPHQL_INTROSPECTIONtrueAllow schema introspection
allow_fragmentsFLUXBASE_GRAPHQL_ALLOW_FRAGMENTSfalseAllow GraphQL fragments
max_fields_per_lvlFLUXBASE_GRAPHQL_MAX_FIELDS_PER_LVL50Maximum fields per nesting level
query {
users {
id
email
name
createdAt
}
}
query {
users(filter: { email_eq: "john@example.com" }) {
id
email
}
}
query {
users(
orderBy: [{ createdAt: DESC }]
limit: 10
offset: 0
) {
id
email
createdAt
}
}
query {
users {
id
email
posts {
id
title
comments {
id
content
author {
email
}
}
}
}
}

The GraphQL API supports PostgREST-compatible filter operators, specified as flat fields in the filter argument:

OperatorDescriptionExample
_eqEqual{ status_eq: "active" }
_neqNot equal{ status_neq: "deleted" }
_gtGreater than{ age_gt: 18 }
_gteGreater than or equal{ age_gte: 18 }
_ltLess than{ price_lt: 100 }
_lteLess than or equal{ price_lte: 100 }
_likePattern match{ name_like: "John%" }
_ilikeCase-insensitive match{ name_ilike: "john%" }
_inIn list{ status_in: ["active", "pending"] }
_is_nullIs null{ deletedAt_is_null: true }

Multiple conditions in a single filter object are combined with AND.

mutation {
insertUser(data: { email: "new@example.com", name: "New User" }) {
id
email
}
}
mutation {
updateUser(id: "user-uuid", data: { name: "Updated Name" }) {
id
name
}
}
mutation {
updateManyUser(filter: { status_eq: "active" }, data: { status: "archived" })
}

updateMany returns an Int count of affected rows.

mutation {
deleteUser(id: "user-uuid") {
id
name
}
}
mutation {
deleteManyUser(filter: { status_eq: "inactive" })
}

deleteMany returns an Int count of deleted rows.

PostgreSQL types are automatically mapped to GraphQL types:

PostgreSQLGraphQL
text, varchar, charString
integer, smallintInt
bigintBigIntScalar (custom)
booleanBoolean
numeric, real, double precisionFloat
uuidUUIDScalar (custom)
json, jsonbJSON (custom scalar)
timestamp, timestamptzDateTime (custom scalar)
dateDate (custom scalar)
array typesJSONScalar

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
}
}
}
}

The Fluxbase CLI provides a graphql command for executing queries and mutations from the command line.

Terminal window
# Execute a query
fluxbase graphql query '{ users { id email } }'
# Query with variables
fluxbase graphql query 'query($id: ID!) { user(id: $id) { email } }' --var 'id=123'
# Execute from file
fluxbase graphql query --file ./query.graphql
# Execute a mutation
fluxbase graphql mutation 'mutation { insertUser(data: {email: "new@example.com"}) { id } }'
# Introspect the schema
fluxbase graphql introspect
# List types only
fluxbase graphql introspect --types

See the CLI Command Reference for complete documentation.


import { createClient } from '@nimbleflux/fluxbase-sdk'
const client = createClient('http://localhost:8080', 'your-anon-key')
// Execute a query
const { data, errors } = await client.graphql.query<UsersQuery>(`
query GetUsers($limit: Int) {
users(limit: $limit) {
id
email
}
}
`, { limit: 10 })
// Execute a mutation
const { data, errors } = await client.graphql.mutation<CreateUserMutation>(`
mutation CreateUser($data: UserInput!) {
insertUser(data: $data) {
id
email
}
}
`, { data: { email: 'new@example.com' } })
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>
)
}

The Query Editor in the Admin Dashboard supports both SQL and GraphQL modes:

  1. Navigate to Query Editor in the sidebar
  2. Click the GraphQL tab at the top of the editor
  3. Write your GraphQL query with auto-completion support
  4. 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

The GraphQL API enforces Row Level Security (RLS) policies exactly like the REST API:

  1. Anonymous users execute queries as the anon PostgreSQL role
  2. Authenticated users execute as the authenticated role
  3. Service role keys bypass RLS for admin operations
  4. Tenant service keys execute as the tenant_service role (tenant-scoped via app.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);

For multi-tenant deployments, use the X-FB-Tenant header to specify the tenant context:

Terminal window
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_id automatically filter data
  • The tenant_admin dashboard role maps to authenticated (respects RLS)
  • The tenant_service role enforces RLS with tenant context
  • The instance_admin role maps to service_role (bypasses RLS)
graphql:
enabled: true
max_depth: 5
max_complexity: 500
introspection: false
allow_fragments: false
max_fields_per_lvl: 50

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
}
}
}
}
}

Query complexity is calculated based on the number of fields and nesting. The max_complexity setting prevents resource-intensive queries.

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
FeatureGraphQLREST
Request formatPOST with query bodyGET/POST/PUT/DELETE
Response shapeExactly what you requestFixed per endpoint
Nested dataSingle requestMultiple requests
CachingRequires client setupHTTP caching
Learning curveHigherLower

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

Ensure GraphQL is enabled in your configuration:

Terminal window
export FLUXBASE_GRAPHQL_ENABLED=true

Reduce the nesting in your query or increase max_depth:

graphql:
max_depth: 15

Check that:

  1. Your JWT token is valid and not expired
  2. RLS policies allow the operation
  3. You’re using the correct role (anon vs authenticated)

Enable introspection (note: disable in production):

graphql:
introspection: true