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 # Disable in production| 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 |
Query Syntax
Section titled “Query Syntax”Basic Query
Section titled “Basic Query”query { users { id email name created_at }}With Filtering
Section titled “With Filtering”query { users(where: { email: { _eq: "john@example.com" } }) { id email }}With Ordering and Pagination
Section titled “With Ordering and Pagination”query { users( order_by: { created_at: desc } limit: 10 offset: 0 ) { id email created_at }}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:
| 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 | { deleted_at: { _is_null: true } } |
_and | Logical AND | { _and: [{ age: { _gte: 18 } }, { status: { _eq: "active" } }] } |
_or | Logical OR | { _or: [{ role: { _eq: "admin" } }, { role: { _eq: "moderator" } }] } |
Mutations
Section titled “Mutations”Insert
Section titled “Insert”mutation { insert_users(objects: [ { email: "new@example.com", name: "New User" } ]) { returning { id email } }}Update
Section titled “Update”mutation { update_users( where: { id: { _eq: "user-uuid" } } _set: { name: "Updated Name" } ) { affected_rows returning { id name } }}Delete
Section titled “Delete”mutation { delete_users(where: { id: { _eq: "user-uuid" } }) { affected_rows }}Upsert (Insert or Update)
Section titled “Upsert (Insert or Update)”mutation { insert_users( objects: [{ id: "existing-uuid", email: "user@example.com", name: "User" }] on_conflict: { constraint: users_pkey update_columns: [name] } ) { returning { id name } }}Type Mapping
Section titled “Type Mapping”PostgreSQL types are automatically mapped to GraphQL types:
| PostgreSQL | GraphQL |
|---|---|
text, varchar, char | String |
integer, smallint | Int |
bigint | String (to preserve precision) |
boolean | Boolean |
numeric, real, double precision | Float |
uuid | ID |
json, jsonb | JSON (custom scalar) |
timestamp, timestamptz | DateTime (custom scalar) |
date | Date (custom scalar) |
array types | [Type] (List) |
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 { insert_users(objects: [{email: "new@example.com"}]) { returning { 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({ url: 'http://localhost:8080' })
// 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!) { insert_users(objects: [$data]) { returning { 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!) { insert_users(objects: [$data]) { returning { 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
Session variables are available in your RLS policies:
-- Example RLS policyCREATE POLICY "Users can view own data" ON users FOR SELECT USING (id = current_setting('request.jwt.claim.sub')::uuid);Security Best Practices
Section titled “Security Best Practices”Production Configuration
Section titled “Production Configuration”graphql: enabled: true max_depth: 5 # Reduce depth in production max_complexity: 500 # Lower complexity limit introspection: false # Disable introspection in productionQuery 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