GraphQL API Guide
GraphQL API Guide
Section titled “GraphQL API Guide”Overview
Section titled “Overview”The API Gateway automatically generates a GraphQL schema from your service contracts with permission-filtered queries and mutations. Services define contracts, and GraphQL operations are created automatically.
Auto-Generated Schema
Section titled “Auto-Generated Schema”Schema Generation
Section titled “Schema Generation”The API Gateway creates GraphQL types from your contracts:
// CreateUserCommand contractCreateUserCommand → mutation createUser(input: CreateUserInput!): User!
// GetUserQuery contractGetUserQuery → query getUser(id: ID!): User
// ListUsersQuery contractListUsersQuery → query listUsers(filter: UserFilter): [User!]!Type Mapping
Section titled “Type Mapping”Contract schemas are converted to GraphQL types:
// Contract input schemainputSchema: { type: 'object', properties: { email: { type: 'string', format: 'email' }, name: { type: 'string' }, role: { type: 'string', enum: ['user', 'admin'] } }}
// Generated GraphQL typeinput CreateUserInput { email: String! name: String! role: UserRole}
enum UserRole { USER ADMIN}Creating GraphQL Operations
Section titled “Creating GraphQL Operations”1. Define Contracts
Section titled “1. Define Contracts”Same contracts power both REST and GraphQL:
import { createContract } from '@banyanai/platform-contract-system';
export const CreateUserContract = createContract({ messageType: 'CreateUserCommand', targetService: 'user-service', inputSchema: { type: 'object', properties: { email: { type: 'string', format: 'email' }, name: { type: 'string' }, role: { type: 'string', enum: ['user', 'admin'] } }, required: ['email', 'name'] }, outputSchema: { type: 'object', properties: { id: { type: 'string' }, email: { type: 'string' }, name: { type: 'string' }, role: { type: 'string' }, createdAt: { type: 'string', format: 'date-time' } } }, requiredPermissions: ['users:create']});
export const GetUserContract = createContract({ messageType: 'GetUserQuery', targetService: 'user-service', inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] }, outputSchema: { type: 'object', properties: { id: { type: 'string' }, email: { type: 'string' }, name: { type: 'string' }, role: { type: 'string' } } }, requiredPermissions: ['users:read']});2. Implement Handlers
Section titled “2. Implement Handlers”Write only business logic (same for REST and GraphQL):
import { QueryHandler } from '@banyanai/platform-cqrs';import { GetUserContract } from '../contracts/UserContracts.js';
@QueryHandler(GetUserContract)export class GetUserHandler { async handle(input: { id: string }) { const user = await this.userRepository.findById(input.id); if (!user) { throw new Error('User not found'); } return user; }}3. Schema Generated Automatically
Section titled “3. Schema Generated Automatically”The API Gateway creates:
type Query { getUser(id: ID!): User listUsers(filter: UserFilter): [User!]!}
type Mutation { createUser(input: CreateUserInput!): User! updateUser(id: ID!, input: UpdateUserInput!): User! deleteUser(id: ID!): Boolean!}
type User { id: ID! email: String! name: String! role: UserRole! createdAt: String!}
input CreateUserInput { email: String! name: String! role: UserRole}
enum UserRole { USER ADMIN}Making GraphQL Requests
Section titled “Making GraphQL Requests”Mutations (Commands)
Section titled “Mutations (Commands)”curl -X POST http://localhost:3003/graphql \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id email name role createdAt } }", "variables": { "input": { "email": "alice@example.com", "name": "Alice Smith", "role": "USER" } } }'Response:
{ "data": { "createUser": { "id": "usr_1234567890", "email": "alice@example.com", "name": "Alice Smith", "role": "USER", "createdAt": "2025-11-15T10:30:00Z" } }}Queries
Section titled “Queries”curl -X POST http://localhost:3003/graphql \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "query GetUser($id: ID!) { getUser(id: $id) { id email name role } }", "variables": { "id": "usr_1234567890" } }'Response:
{ "data": { "getUser": { "id": "usr_1234567890", "email": "alice@example.com", "name": "Alice Smith", "role": "USER" } }}Multiple Operations
Section titled “Multiple Operations”query GetUserData($userId: ID!) { user: getUser(id: $userId) { id email name role }
users: listUsers(filter: { role: ADMIN }) { id name email }}Permission-Filtered Schema
Section titled “Permission-Filtered Schema”Key Feature: Users only see operations they can access.
Example
Section titled “Example”User with users:read permission sees:
type Query { getUser(id: ID!): User listUsers(filter: UserFilter): [User!]!}User with both users:read and users:create sees:
type Query { getUser(id: ID!): User listUsers(filter: UserFilter): [User!]!}
type Mutation { createUser(input: CreateUserInput!): User!}Implementation: The API Gateway filters the schema based on JWT token permissions before serving it.
GraphQL Playground
Section titled “GraphQL Playground”Interactive GraphQL explorer available at:
http://localhost:3003/graphqlFeatures:
- Schema introspection
- Auto-completion
- Query validation
- Documentation browser
- Variable editor
Using the Playground
Section titled “Using the Playground”- Open
http://localhost:3003/graphqlin browser - Add auth header in HTTP Headers tab:
{ "Authorization": "Bearer YOUR_JWT_TOKEN"}- Write query with auto-completion
- Execute and see results
GraphQL Subscriptions (Events)
Section titled “GraphQL Subscriptions (Events)”Subscribe to real-time events via GraphQL subscriptions:
subscription OnUserCreated { userCreated { id email name role }}Implementation: Uses WebSocket protocol under the hood.
See WebSocket documentation for more details on event subscriptions.
Error Handling
Section titled “Error Handling”GraphQL errors follow standard format:
{ "errors": [ { "message": "User not found", "path": ["getUser"], "extensions": { "code": "NOT_FOUND", "correlationId": "cor_abc123xyz", "timestamp": "2025-11-15T10:30:00Z" } } ], "data": { "getUser": null }}Error Codes
Section titled “Error Codes”| Code | Description |
|---|---|
| UNAUTHENTICATED | Missing or invalid JWT |
| FORBIDDEN | Insufficient permissions |
| BAD_USER_INPUT | Validation error |
| NOT_FOUND | Resource doesn’t exist |
| INTERNAL_SERVER_ERROR | Handler exception |
Field Name Sanitization
Section titled “Field Name Sanitization”The API Gateway sanitizes field names to be GraphQL-compliant:
// Contract field with dotsproperties: { 'user.email': { type: 'string' }}
// Converted to GraphQLuserEmail: StringPattern: Dots (.) are removed and the next character is capitalized.
Introspection
Section titled “Introspection”Query the schema programmatically:
query IntrospectionQuery { __schema { types { name fields { name type { name kind } } } }}Best Practices
Section titled “Best Practices”1. Design Clear Operation Names
Section titled “1. Design Clear Operation Names”// Good: Clear, descriptiveCreateUserCommand → createUserGetUserQuery → getUserListUsersQuery → listUsers
// Avoid: AmbiguousProcessUserCommand → processUser (what does it do?)2. Use Input Types for Mutations
Section titled “2. Use Input Types for Mutations”// Good: Structured inputmutation createUser(input: CreateUserInput!)
// Avoid: Too many argumentsmutation createUser(email: String!, name: String!, role: String!, ...)3. Provide Rich Output Types
Section titled “3. Provide Rich Output Types”outputSchema: { type: 'object', properties: { id: { type: 'string' }, email: { type: 'string' }, name: { type: 'string' }, role: { type: 'string' }, createdAt: { type: 'string', format: 'date-time' }, updatedAt: { type: 'string', format: 'date-time' }, // Include useful fields clients might need }}4. Set Granular Permissions
Section titled “4. Set Granular Permissions”// Read operationsrequiredPermissions: ['users:read']
// Write operationsrequiredPermissions: ['users:create']
// Admin operationsrequiredPermissions: ['users:admin']5. Use Enums for Constrained Values
Section titled “5. Use Enums for Constrained Values”inputSchema: { properties: { role: { type: 'string', enum: ['user', 'admin', 'moderator'] // → GraphQL enum } }}Client Libraries
Section titled “Client Libraries”Apollo Client (React)
Section titled “Apollo Client (React)”import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
const client = new ApolloClient({ uri: 'http://localhost:3003/graphql', cache: new InMemoryCache(), headers: { Authorization: `Bearer ${jwtToken}` }});
const CREATE_USER = gql` mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id email name } }`;
const { data } = await client.mutate({ mutation: CREATE_USER, variables: { input: { email: 'alice@example.com', name: 'Alice Smith' } }});GraphQL Request (Node.js)
Section titled “GraphQL Request (Node.js)”import { request, gql } from 'graphql-request';
const query = gql` query GetUser($id: ID!) { getUser(id: $id) { id email name } }`;
const data = await request( 'http://localhost:3003/graphql', query, { id: 'usr_123' }, { Authorization: `Bearer ${jwtToken}` });Troubleshooting
Section titled “Troubleshooting”Operation Not in Schema
Section titled “Operation Not in Schema”Cause: User lacks required permissions or contract not discovered
Solution:
- Check JWT token contains required permissions
- Verify contract is registered:
curl http://localhost:3003/debug/contracts
Invalid Field Names
Section titled “Invalid Field Names”Cause: Contract field names contain invalid GraphQL characters
Solution: API Gateway sanitizes automatically, but avoid special characters in field names:
// Avoidproperties: { 'user.email.address': { type: 'string' } // Too many dots}
// Preferproperties: { email: { type: 'string' }}Type Conflicts
Section titled “Type Conflicts”Cause: Multiple contracts generate same type name
Solution: Use unique message type prefixes:
// Service AmessageType: 'UserServiceCreateUser'
// Service BmessageType: 'AuthServiceCreateUser'Next Steps
Section titled “Next Steps”- REST API Guide - Alternative to GraphQL
- API Contracts - Contract best practices
- API Security - Authentication and authorization