API Reference
The Productify Framework Manager provides both GraphQL and REST APIs for programmatic access to all platform features.
Base URLs
- GraphQL Endpoint:
POST /query - GraphQL Playground (dev only):
GET /gql - REST API:
/api/*
Authentication
The Manager supports three authentication methods:
1. Bearer Token (Personal Access Tokens)
Personal Access Tokens (PATs) provide secure API access for users.
Header Format:
Authorization: Bearer <your-token>Creating a PAT:
mutation {
createPersonalAccessToken(
name: "My API Token"
expiresAt: "2025-12-31T23:59:59Z"
) {
token
personalAccessToken {
id
name
tokenPrefix
expiresAt
createdAt
}
}
}WARNING
The token is only shown once during creation and cannot be retrieved later. Store it securely.
2. Machine User Authentication
Machine users enable backend services to authenticate with the Manager.
Bearer Token (Recommended):
Authorization: Bearer <machine-user-token>Basic Authentication:
Authorization: Basic <base64(username:password)>Creating a Machine User:
mutation {
createMachineUserWithCredentials(
tenantId: "tenant-uuid"
input: {
name: "Backend Service"
username: "backend-service"
hashedKey: "__GENERATE_TOKEN__"
enabled: true
}
) {
generatedToken
machineUser {
id
username
}
}
}3. User Session (OAuth/OIDC)
Standard web session authentication via OAuth/OIDC providers configured in the Manager.
GraphQL API
Schema Overview
The GraphQL API provides type-safe access to all Manager resources with filtering, ordering, and pagination support.
GraphQL Playground: Access at /gql in development mode to explore the schema interactively.
Common Types
Filter
input Filter {
field: String!
value: String!
}Order
input Order {
field: String!
direction: OrderDirection!
}
enum OrderDirection {
ASC
DESC
}Pagination
input Pagination {
limit: Int!
offset: Int!
}Endpoint Types
Authentication Types:
enum EndpointAuthType {
Basic # HTTP Basic Authentication
Bearer # Bearer Token Authentication
None # No authentication required
}Message Types:
enum EndpointInputMessageType {
SOAP # SOAP XML message
XML # Plain XML
JSON # JSON payload
}
enum EndpointOutputMessageType {
SOAP # SOAP XML message
XML # Plain XML
JSON # JSON payload
}Log Levels:
enum EndpointLogLevel {
NONE # No logging
BASIC # Log metadata only (headers, status, timing)
FULL # Log full request/response bodies
}Queries
Projects
List Projects
query {
projects(
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 10, offset: 0 }
) {
id
name
slug
description
createdAt
}
}Get Project by Slug
query {
project(slug: "my-project") {
id
name
slug
description
tenants {
id
name
}
}
}Tenants
List Tenants
query {
tenants(
projectSlug: "my-project"
filters: []
order: { field: "name", direction: ASC }
pagination: { limit: 20, offset: 0 }
) {
id
name
slug
description
}
}Get Tenant
query {
tenant(projectId: "project-uuid", slug: "tenant-slug") {
id
name
slug
applications {
id
name
}
}
}Applications
List Applications
query {
applications(
tenantSlug: "tenant-slug"
filters: []
order: { field: "name", direction: ASC }
pagination: { limit: 10, offset: 0 }
) {
id
name
slug
description
endpoints {
id
name
url
}
}
}Get Application
query {
application(
projectId: "project-uuid"
tenantId: "tenant-uuid"
slug: "app-slug"
) {
id
name
slug
description
triggers {
id
name
cronExpression
enabled
}
}
}Settings
Get Settings (Inherited)
query {
settings(
project: "project-uuid"
tenant: "tenant-uuid"
application: "app-uuid"
) {
id
backendSetting {
id
moduleConfig {
EnabledModules
KeyValuePairs {
Key
Value
}
}
}
frontendSetting {
id
moduleConfig {
EnabledThemes
DefaultTheme
EnabledLocales
DefaultLocale
}
}
}
}Configuration Inheritance
Settings follow a three-layer hierarchy: Project → Tenant → Application. Query at any level to get the effective merged configuration.
Endpoints
List Endpoints
query {
endpoints(
application: "app-uuid"
filters: []
order: { field: "name", direction: ASC }
pagination: { limit: 50, offset: 0 }
) {
id
name
description
enabled
url
consumeURL
authType
}
}Triggers
List Triggers
query {
triggers(
application: "app-uuid"
filters: []
order: { field: "name", direction: ASC }
pagination: { limit: 50, offset: 0 }
) {
id
name
cronExpression
enabled
createdAt
updatedAt
}
}Machine Users
List Machine Users
query {
machineUsers(
tenant: "tenant-uuid"
filters: []
order: { field: "name", direction: ASC }
pagination: { limit: 20, offset: 0 }
) {
id
name
username
enabled
tokenPrefix
}
}Language Packs
List Language Packs
query {
languagePacks(
project: "project-uuid"
filters: []
order: { field: "name", direction: ASC }
pagination: { limit: 20, offset: 0 }
) {
id
name
content {
Key
Value
}
}
}Personal Access Tokens
List My Personal Access Tokens
query {
myPersonalAccessTokens(
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
name
tokenPrefix
expiresAt
createdAt
lastUsedAt
}
}List Personal Access Tokens (Cursor-based)
query {
personalAccessTokens(first: 20, after: "cursor-string") {
edges {
node {
id
name
tokenPrefix
expiresAt
createdAt
lastUsedAt
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}Token Security
Only the token prefix is visible after creation. The full token is only shown once during creation.
Users
Get Current User
query {
me {
id
username
email
displayName
disabled
}
}List Users
query {
users(
filters: []
order: { field: "username", direction: ASC }
pagination: { limit: 20, offset: 0 }
) {
id
username
email
displayName
}
}Search Provider Users
query {
userProviderSearch(name: "john") {
id
username
email
displayName
disabled
}
}User Relationships
List User-Application Relations
query {
user2applications(
application: "app-uuid"
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
role
user {
id
username
email
}
application {
id
name
}
}
}Get User-Application Relation
query {
user2application(application: "app-uuid", id: "relation-uuid") {
id
role
user {
username
}
}
}List User-Project Relations
query {
user2projects(
project: "project-uuid"
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
role
user {
id
username
email
}
project {
id
name
}
}
}Get User-Project Relation
query {
user2project(project: "project-uuid", id: "relation-uuid") {
id
role
user {
username
}
}
}List User-Tenant Relations
query {
user2tenants(
tenant: "tenant-uuid"
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
role
user {
id
username
email
}
tenant {
id
name
}
}
}Get User-Tenant Relation
query {
user2tenant(tenant: "tenant-uuid", id: "relation-uuid") {
id
role
user {
username
}
}
}Machine User to Endpoint Relations
List Machine User-Endpoint Relations
query {
machineUser2endpoints(
endpoint: "endpoint-uuid"
filters: []
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
enabled
machineUser {
id
username
}
endpoint {
id
name
}
}
}Get Machine User-Endpoint Relation
query {
machineUser2endpoint(endpoint: "endpoint-uuid", id: "relation-uuid") {
id
enabled
}
}Audit Logs
List Audit Logs
query {
auditLogs(
projectID: "project-uuid"
filters: [{ field: "action", value: "create" }]
order: { field: "created_at", direction: DESC }
pagination: { limit: 50, offset: 0 }
) {
id
entityType
entityID
action
description
metadata
changes
ipAddress
userAgent
createdAt
user {
username
}
}
}Pilet Packages
List Pilet Packages
query {
piletPackages(projectId: "project-uuid", enabled: true) {
packages {
id
name
version
description
size
hash
enabled
createdAt
updatedAt
}
count
}
}Get Pilet Package
query {
piletPackage(
projectId: "project-uuid"
name: "@my-org/my-pilet"
version: "1.0.0"
) {
id
name
version
description
size
hash
enabled
createdAt
updatedAt
}
}Mutations
Project Management
Create Project
mutation {
createProject(input: { name: "My Project", slug: "my-project" }) {
id
name
slug
}
}Update Project
mutation {
updateProject(
id: "project-uuid"
input: { name: "Updated Project Name", description: "New description" }
) {
id
name
description
}
}Delete Project
mutation {
deleteProject(id: "project-uuid")
}Tenant Management
Create Tenant
mutation {
createTenant(
projectId: "project-uuid"
input: { name: "Customer A", slug: "customer-a", type: run }
) {
id
name
slug
type
}
}Update Tenant
mutation {
updateTenant(id: "tenant-uuid", input: { name: "Updated Customer Name" }) {
id
name
}
}Delete Tenant
mutation {
deleteTenant(id: "tenant-uuid")
}Application Management
Create Application
mutation {
createApplication(
tenantId: "tenant-uuid"
input: {
name: "Web App"
slug: "web-app"
description: "Main web application"
}
) {
id
name
slug
}
}Update Application
mutation {
updateApplication(
id: "app-uuid"
input: { description: "Updated description" }
) {
id
description
}
}Delete Application
mutation {
deleteApplication(id: "app-uuid")
}Promote Application
Promote/copy an application version to another tenant (environment):
mutation {
promoteApplication(
input: {
applicationId: "app-uuid"
targetTenantId: "prod-tenant-uuid"
slug: "app-slug"
name: "Application Name"
version: "1.0.0"
}
) {
id
name
slug
version
}
}Copy Application
Copy an application to another tenant with a new slug:
mutation {
copyApplication(
input: {
sourceApplicationId: "app-uuid"
targetTenantId: "new-tenant-uuid"
slug: "new-app-slug"
name: "New Application Name"
version: "1.0.0"
}
) {
id
name
slug
version
}
}Settings Management
Configuration Inheritance
Settings can be defined at three levels: Project → Tenant → Application. Use the create/update mutations to define settings at specific levels, and use the settings query to retrieve the effective merged configuration.
Create Backend Settings
mutation {
createBackendSettings(
input: {
projectID: "project-uuid" # or tenantID or applicationID
moduleConfig: {
EnabledModules: ["auth", "notifications"]
KeyValuePairs: [
{ Key: "api_timeout", Value: "30" }
{ Key: "max_retries", Value: "3" }
]
}
}
) {
id
moduleConfig {
EnabledModules
KeyValuePairs {
Key
Value
}
}
}
}Update Backend Settings
mutation {
updateBackendSettings(
id: "backend-settings-uuid"
input: {
moduleConfig: {
EnabledModules: ["auth", "notifications", "analytics"]
KeyValuePairs: [{ Key: "api_timeout", Value: "60" }]
}
}
) {
id
moduleConfig {
EnabledModules
}
}
}Delete Backend Settings
mutation {
deleteBackendSettings(id: "backend-settings-uuid")
}Create Frontend Settings
mutation {
createFrontendSettings(
input: {
applicationID: "app-uuid" # or projectID or tenantID
moduleConfig: {
EnabledThemes: ["light", "dark"]
DefaultTheme: "light"
EnabledLocales: ["en", "hu", "de"]
DefaultLocale: "en"
EnabledModules: ["dashboard", "reports"]
KeyValuePairs: [{ Key: "show_footer", Value: "true" }]
}
}
) {
id
moduleConfig {
DefaultTheme
DefaultLocale
EnabledThemes
EnabledLocales
}
}
}Update Frontend Settings
mutation {
updateFrontendSettings(
id: "frontend-settings-uuid"
input: {
moduleConfig: {
DefaultTheme: "dark"
EnabledModules: ["dashboard", "reports", "analytics"]
}
}
) {
id
moduleConfig {
DefaultTheme
EnabledModules
}
}
}Delete Frontend Settings
mutation {
deleteFrontendSettings(id: "frontend-settings-uuid")
}Update Backend Module Configuration
mutation {
updateBackendModuleConfig(
id: "backend-settings-uuid"
input: {
EnabledModules: ["module1", "module2"]
KeyValuePairs: [
{ Key: "feature_flag_1", Value: "true" }
{ Key: "api_timeout", Value: "30" }
]
}
) {
id
moduleConfig {
EnabledModules
KeyValuePairs {
Key
Value
}
}
}
}Update Frontend Module Configuration
mutation {
updateFrontendModuleConfig(
id: "frontend-settings-uuid"
input: {
EnabledThemes: ["dark", "light"]
DefaultTheme: "light"
EnabledLocales: ["en", "hu"]
DefaultLocale: "en"
KeyValuePairs: [{ Key: "show_footer", Value: "true" }]
}
) {
id
moduleConfig {
DefaultTheme
DefaultLocale
}
}
}Language Pack Management
Create Language Pack
mutation {
createLanguagePack(projectId: "project-uuid", input: { name: "en-US" }) {
id
name
}
}Update Language Pack Content
mutation {
updateLanguagePackContent(
id: "language-pack-uuid"
input: {
content: [
{ Key: "welcome", Value: "Welcome" }
{ Key: "login", Value: "Login" }
{ Key: "logout", Value: "Logout" }
]
}
) {
id
content {
Key
Value
}
}
}Endpoint Management
Create Endpoint
mutation {
createEndpoint(
applicationId: "app-uuid"
input: {
name: "Webhook Handler"
description: "Processes incoming webhooks"
enabled: true
consumeURL: "/api/webhook"
url: "https://backend.example.com/webhook"
authType: Bearer
inputMessageType: JSON
outputMessageType: JSON
logLevel: BASIC
}
) {
id
name
consumeURL
url
authType
inputMessageType
outputMessageType
logLevel
}
}Field Descriptions:
consumeURL- The path pattern that will trigger this endpoint (e.g.,/api/webhook)url- The backend URL to forward requests toauthType- Authentication method:Basic,Bearer, orNoneinputMessageType- Expected input format:JSON,XML, orSOAP(optional)outputMessageType- Output format after transformation:JSON,XML, orSOAP(optional)logLevel- ESB logging verbosity:NONE,BASIC, orFULL(default:BASIC)
Update Endpoint
mutation {
updateEndpoint(
id: "endpoint-uuid"
input: {
enabled: false
url: "https://new-backend.example.com/webhook"
logLevel: FULL
}
) {
id
enabled
url
logLevel
}
}Delete Endpoint
mutation {
deleteEndpoint(id: "endpoint-uuid")
}Trigger Management
Create Trigger
mutation {
createTrigger(
applicationId: "app-uuid"
input: {
name: "Daily Report"
description: "Generates daily report"
cronExpression: "0 0 * * *"
runKey: "daily-report"
enabled: true
}
) {
id
name
cronExpression
runKey
}
}Update Trigger
mutation {
updateTrigger(
id: "trigger-uuid"
input: { enabled: false, cronExpression: "0 2 * * *" }
) {
id
enabled
cronExpression
}
}Machine User Management
Create Machine User with Token
mutation {
createMachineUserWithCredentials(
tenantId: "tenant-uuid"
input: {
name: "API Service"
username: "api-service"
hashedKey: "__GENERATE_TOKEN__"
enabled: true
}
) {
generatedToken
machineUser {
id
username
tokenPrefix
}
}
}Token Generation
Use hashedKey: "__GENERATE_TOKEN__" to auto-generate a Bearer token. The plaintext token is only returned once.
Update Machine User
mutation {
updateMachineUser(id: "machine-user-uuid", input: { enabled: false }) {
id
enabled
}
}Delete Machine User
mutation {
deleteMachineUser(id: "machine-user-uuid")
}Machine User to Endpoint Access
Create Machine User-Endpoint Access
mutation {
createMachineUser2Endpoint(
input: {
machineUserID: "machine-user-uuid"
endpointID: "endpoint-uuid"
enabled: true
}
) {
id
enabled
machineUser {
username
}
endpoint {
name
}
}
}Update Machine User-Endpoint Access
mutation {
updateMachineUser2Endpoint(id: "relation-uuid", input: { enabled: false }) {
id
enabled
}
}Delete Machine User-Endpoint Access
mutation {
deleteMachineUser2Endpoint(id: "relation-uuid")
}Personal Access Token Management
Create Personal Access Token
mutation {
createPersonalAccessToken(
name: "Production API Token"
expiresAt: "2025-12-31T23:59:59Z"
) {
token
personalAccessToken {
id
name
tokenPrefix
expiresAt
createdAt
}
}
}One-Time Token Display
The token field contains the full plaintext token and is only returned once. Store it securely.
Revoke Personal Access Token
mutation {
revokePersonalAccessToken(id: "pat-uuid") {
id
name
revoked
}
}Delete Personal Access Token
mutation {
deletePersonalAccessToken(id: "pat-uuid")
}User Access Management
Simplified User Management
The following mutations provide a simplified interface for user access management. For advanced use cases, low-level mutations (createUser2Project, updateUser2Project, etc.) are also available but generally not recommended for direct use.
Add User to Project
mutation {
addUserToProject(
projectID: "project-uuid"
userID: "user-uuid"
role: admin
) {
id
role
user {
username
}
}
}Available Roles: admin, editor, viewer
Update User Project Role
mutation {
updateUserProjectRole(id: "relation-uuid", role: viewer) {
id
role
}
}Remove User from Project
mutation {
removeUserFromProject(id: "relation-uuid")
}Add User to Tenant
mutation {
addUserToTenant(tenantID: "tenant-uuid", userID: "user-uuid", role: admin) {
id
role
}
}Available Roles: admin, editor, viewer
Update User Tenant Role
mutation {
updateUserTenantRole(id: "relation-uuid", role: editor) {
id
role
}
}Remove User from Tenant
mutation {
removeUserFromTenant(id: "relation-uuid")
}Add User to Application
mutation {
addUserToApplication(
applicationID: "app-uuid"
userID: "user-uuid"
role: editor
) {
id
role
}
}Available Roles: admin, editor, viewer
Update User Application Role
mutation {
updateUserApplicationRole(id: "relation-uuid", role: viewer) {
id
role
}
}Remove User from Application
mutation {
removeUserFromApplication(id: "relation-uuid")
}Pilet Package Management
Update Pilet Package
mutation {
updatePiletPackage(
projectId: "project-uuid"
name: "@my-org/my-pilet"
version: "1.0.0"
description: "Updated description"
enabled: true
) {
id
name
version
description
enabled
}
}Pilet Upload
The actual pilet file upload happens via the REST API endpoint POST /pilets/:projectId/:name/:version. This mutation is for metadata updates only.
Enable Pilet Package
mutation {
enablePiletPackage(
projectId: "project-uuid"
name: "@my-org/my-pilet"
version: "1.0.0"
) {
id
enabled
}
}Disable Pilet Package
mutation {
disablePiletPackage(
projectId: "project-uuid"
name: "@my-org/my-pilet"
version: "1.0.0"
) {
id
enabled
}
}REST API
Machine User Validation
Endpoint: POST /api/validate-machine-user
Description: Validates machine user credentials. Used by the Proxy for authentication.
Authentication: None required (internal use)
Request Body:
{
"username": "machine-user-name",
"token": "bearer-token-or-password"
}Response:
{
"valid": true,
"tenantId": "tenant-uuid",
"machineUserId": "machine-user-uuid"
}Check Machine User Auth
Endpoint: GET /api/machine/check
Description: Verifies machine user authentication status.
Authentication: Bearer Token (Machine User)
Response:
{
"authenticated": true,
"machineUserId": "machine-user-uuid",
"tenantId": "tenant-uuid"
}Register Backend
Endpoint: POST /api/machine/register-backend
Description: Registers a backend service for trigger execution callbacks.
Authentication: Bearer Token (Machine User)
Request Body:
{
"name": "Payment Service",
"callbackUrl": "https://payment.example.com/triggers/callback",
"description": "Handles payment processing triggers"
}Response:
{
"id": "backend-uuid",
"name": "Payment Service",
"callbackUrl": "https://payment.example.com/triggers/callback",
"machineUserId": "machine-user-uuid"
}Validate Proxy Auth
Endpoint: POST /api/validate-proxy-auth
Description: Validates user authentication for the Proxy. Used internally by the Proxy to validate session tokens.
Authentication: None required (internal use)
Request Body:
{
"token": "session-token-or-cookie"
}Response:
{
"valid": true,
"userId": "user-uuid",
"username": "user@example.com"
}Frontend Configuration
Endpoint: GET /api/frontend-config/:application_id
Description: Retrieves frontend configuration for an application, including theme, locale, and enabled modules.
Authentication: None required (public endpoint, used by Proxy)
Response:
{
"applicationId": "app-uuid",
"moduleConfig": {
"EnabledModules": ["module1", "module2"],
"EnabledThemes": ["light", "dark"],
"DefaultTheme": "light",
"EnabledLocales": ["en", "hu"],
"DefaultLocale": "en",
"KeyValuePairs": [{ "Key": "feature_flag", "Value": "true" }]
}
}Pilet Feed
Endpoint: GET /pilet-feed/:applicationId
Description: Returns the Piral-compatible pilet feed for an application. This endpoint is used by the frontend to discover and load pilets (microfrontends).
Authentication: None required (public endpoint)
Response:
{
"items": [
{
"name": "@my-org/my-pilet",
"version": "1.0.0",
"link": "https://manager.example.com/pilets/project-uuid/@my-org/my-pilet/1.0.0",
"hash": "sha256-abcd1234...",
"spec": "v2"
}
]
}Pilet Package Management
List Pilets
Endpoint: GET /pilets/:projectId
Description: Lists all pilet packages for a project.
Authentication: Bearer Token (Personal Access Token or User Session)
Query Parameters:
enabled(optional): Filter by enabled status (true/false)
Response:
{
"packages": [
{
"id": "pilet-uuid",
"name": "@my-org/my-pilet",
"version": "1.0.0",
"description": "My pilet description",
"size": 123456,
"hash": "sha256-abcd1234...",
"enabled": true,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-02T00:00:00Z"
}
],
"count": 1
}Upload Pilet
Endpoint: POST /pilets/:projectId/:name/:version
Description: Uploads a new pilet package or updates an existing one.
Authentication: Bearer Token (Personal Access Token or User Session)
Request:
- Content-Type:
multipart/form-data - File field:
file(the .tgz pilet package) - Optional field:
description(string)
Response:
{
"id": "pilet-uuid",
"name": "@my-org/my-pilet",
"version": "1.0.0",
"size": 123456,
"hash": "sha256-abcd1234...",
"enabled": true
}Download Pilet
Endpoint: GET /pilets/:projectId/:name/:version
Description: Downloads a pilet package file.
Authentication: Bearer Token (Personal Access Token or User Session)
Response: Binary .tgz file
Headers:
Content-Type: application/gzipContent-Disposition: attachment; filename="<name>-<version>.tgz"
Delete Pilet
Endpoint: DELETE /pilets/:projectId/:name/:version
Description: Deletes a pilet package.
Authentication: Bearer Token (Personal Access Token or User Session)
Response:
{
"success": true
}ESB (Enterprise Service Bus) Endpoints
Endpoint: ANY /esb/:applicationId/*endpointPath
Description: Routes requests to registered backend endpoints through the ESB. Supports all HTTP methods (GET, POST, PUT, DELETE, etc.).
Authentication: Handled by Proxy (expects X-User-ID header)
Headers:
X-User-ID: User ID (set by Proxy)X-Application-ID: Application ID (optional)X-Tenant-ID: Tenant ID (optional)
Example:
# Request to ESB
GET /esb/app-uuid/api/users/123
# Routed to configured backend endpoint
# Based on endpoint configuration with consumeURL matching "/api/users/*"Error Handling
GraphQL Errors
GraphQL errors follow this structure:
{
"errors": [
{
"message": "Project not found",
"path": ["project"],
"extensions": {
"code": "NOT_FOUND"
}
}
],
"data": null
}Common Error Codes:
UNAUTHENTICATED- Missing or invalid authenticationFORBIDDEN- Insufficient permissionsNOT_FOUND- Resource not foundBAD_USER_INPUT- Invalid input dataINTERNAL_SERVER_ERROR- Server error
REST API Errors
REST endpoints return HTTP status codes with error details:
{
"error": "Invalid credentials",
"code": "INVALID_CREDENTIALS",
"status": 401
}Common HTTP Status Codes:
200- Success400- Bad Request401- Unauthorized403- Forbidden404- Not Found500- Internal Server Error
Rate Limiting
Currently, there are no hard rate limits enforced. For production deployments, it's recommended to implement rate limiting at the reverse proxy level (e.g., using Caddy or nginx).
Best Practices
1. Use Filtering and Pagination
Always use pagination for list queries to avoid performance issues:
query {
projects(
filters: [{ field: "name", value: "prod" }]
order: { field: "created_at", direction: DESC }
pagination: { limit: 20, offset: 0 }
) {
id
name
}
}2. Request Only Required Fields
GraphQL allows you to request only the fields you need:
query {
project(slug: "my-project") {
id
name
# Don't fetch relations unless needed
}
}3. Secure Token Storage
- Never commit tokens to version control
- Use environment variables or secret management systems
- Rotate tokens regularly
- Use expiration dates for PATs
4. Handle Errors Gracefully
Always check for errors in GraphQL responses:
const response = await fetch("/query", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ query, variables }),
});
const { data, errors } = await response.json();
if (errors) {
// Handle errors
console.error("GraphQL errors:", errors);
}5. Use Machine Users for Services
For backend services and integrations, always use machine users instead of personal access tokens. Machine users provide:
- Tenant-scoped access
- No expiration (unless disabled)
- Better audit trails
- Service account semantics
Examples
Complete Project Setup
# 1. Create project
mutation {
createProject(input: { name: "E-commerce Platform", slug: "ecommerce" }) {
id
}
}
# 2. Create tenant
mutation {
createTenant(
projectId: "project-uuid"
input: { name: "Store A", slug: "store-a", type: run }
) {
id
}
}
# 3. Create application
mutation {
createApplication(
tenantId: "tenant-uuid"
input: {
name: "Storefront"
slug: "storefront"
description: "Customer-facing web application"
}
) {
id
}
}
# 4. Configure settings
mutation {
updateFrontendModuleConfig(
id: "settings-uuid"
input: {
EnabledThemes: ["light", "dark"]
DefaultTheme: "light"
DefaultLocale: "en"
}
) {
id
}
}Trigger-Based Workflow
# 1. Create backend endpoint
mutation {
createEndpoint(
applicationId: "app-uuid"
input: {
name: "Order Processor"
description: "Processes orders"
consumeURL: "/api/orders/process"
url: "https://backend.example.com/process-orders"
enabled: true
authType: Bearer
}
) {
id
}
}
# 2. Create trigger
mutation {
createTrigger(
applicationId: "app-uuid"
input: {
name: "Hourly Order Processing"
description: "Processes orders hourly"
cronExpression: "0 * * * *"
runKey: "hourly-orders"
enabled: true
}
) {
id
}
}
# 3. Register backend for callbacks
# (REST API call with machine user token)
POST /api/machine/register-backend
{
"name": "Order Service",
"callbackUrl": "https://backend.example.com/triggers/callback"
}API Versioning
The GraphQL API is unversioned but follows these principles:
- Additive changes - New fields and types are added without breaking existing queries
- Deprecation - Fields are marked
@deprecatedbefore removal - No breaking changes - Existing fields maintain their contracts
For REST endpoints, the current version is v1 (implicit). Future versions will be explicitly versioned in the URL path.