API Contracts Guide
API Contracts Guide
Section titled “API Contracts Guide”Overview
Section titled “Overview”API contracts define the public interface of your services, automatically generating REST and GraphQL endpoints. Well-designed contracts are the foundation of a good API.
Contract Anatomy
Section titled “Contract Anatomy”A complete API contract includes:
import { createContract } from '@banyanai/platform-contract-system';
export const CreateUserContract = createContract({ // 1. Message Type (determines operation name) messageType: 'CreateUserCommand',
// 2. Target Service (routing) targetService: 'user-service',
// 3. Input Schema (validation) inputSchema: { type: 'object', properties: { email: { type: 'string', format: 'email' }, name: { type: 'string', minLength: 1, maxLength: 100 }, role: { type: 'string', enum: ['user', 'admin'] } }, required: ['email', 'name'] },
// 4. Output Schema (response shape) outputSchema: { type: 'object', properties: { id: { type: 'string' }, email: { type: 'string' }, name: { type: 'string' }, role: { type: 'string' }, createdAt: { type: 'string', format: 'date-time' } }, required: ['id', 'email', 'name', 'role', 'createdAt'] },
// 5. Permissions (authorization) requiredPermissions: ['users:create'],
// 6. Public Flag (optional, defaults to false) isPublic: false});Message Type Naming
Section titled “Message Type Naming”Convention
Section titled “Convention”Message type determines the generated endpoint name:
| Message Type | REST Endpoint | GraphQL Operation |
|---|---|---|
CreateUserCommand | POST /api/users | mutation createUser |
GetUserQuery | GET /api/users/:id | query getUser |
UpdateUserCommand | PUT /api/users/:id | mutation updateUser |
DeleteUserCommand | DELETE /api/users/:id | mutation deleteUser |
ListUsersQuery | GET /api/users | query listUsers |
Best Practices
Section titled “Best Practices”// Good: Clear intent, follows conventionmessageType: 'CreateUserCommand'messageType: 'GetUserQuery'messageType: 'UpdateUserCommand'
// Avoid: Ambiguous or non-standardmessageType: 'ProcessUser'messageType: 'HandleUserRequest'Input Schema
Section titled “Input Schema”JSON Schema Format
Section titled “JSON Schema Format”Input schemas use JSON Schema for validation:
inputSchema: { type: 'object', properties: { // String validation email: { type: 'string', format: 'email', maxLength: 255, description: 'User email address' },
// Number validation age: { type: 'number', minimum: 0, maximum: 150, description: 'User age in years' },
// Enum validation role: { type: 'string', enum: ['user', 'admin', 'moderator'], description: 'User role' },
// Array validation tags: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 10, description: 'User tags' },
// Nested object address: { type: 'object', properties: { street: { type: 'string' }, city: { type: 'string' }, zipCode: { type: 'string', pattern: '^[0-9]{5}$' } }, required: ['city'] } }, required: ['email', 'role']}Common Formats
Section titled “Common Formats”// Email{ type: 'string', format: 'email' }
// Date-time (ISO 8601){ type: 'string', format: 'date-time' }
// URI{ type: 'string', format: 'uri' }
// UUID{ type: 'string', format: 'uuid' }
// Date (YYYY-MM-DD){ type: 'string', format: 'date' }
// Time (HH:MM:SS){ type: 'string', format: 'time' }Validation Examples
Section titled “Validation Examples”// Strong passwordpassword: { type: 'string', minLength: 8, maxLength: 100, pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).*$', description: 'Must contain uppercase, lowercase, and number'}
// Phone numberphoneNumber: { type: 'string', pattern: '^\\+[1-9]\\d{1,14}$', description: 'E.164 format phone number'}
// Positive integerquantity: { type: 'integer', minimum: 1, description: 'Must be at least 1'}
// Percentagediscount: { type: 'number', minimum: 0, maximum: 100, description: 'Discount percentage (0-100)'}Output Schema
Section titled “Output Schema”Response Shape
Section titled “Response Shape”Define the complete response structure:
outputSchema: { type: 'object', properties: { // Always include ID id: { type: 'string', description: 'Unique identifier' },
// Core data fields email: { type: 'string' }, name: { type: 'string' }, role: { type: 'string' },
// Timestamps (ISO 8601 strings) createdAt: { type: 'string', format: 'date-time', description: 'Creation timestamp' }, updatedAt: { type: 'string', format: 'date-time', description: 'Last update timestamp' },
// Optional fields (not in required array) phoneNumber: { type: 'string', description: 'Optional phone number' } }, required: ['id', 'email', 'name', 'role', 'createdAt']}Nested Responses
Section titled “Nested Responses”outputSchema: { type: 'object', properties: { id: { type: 'string' }, email: { type: 'string' },
// Nested object profile: { type: 'object', properties: { bio: { type: 'string' }, avatar: { type: 'string', format: 'uri' } } },
// Array of objects addresses: { type: 'array', items: { type: 'object', properties: { street: { type: 'string' }, city: { type: 'string' }, isPrimary: { type: 'boolean' } } } } }}Permissions
Section titled “Permissions”Permission-Based Access
Section titled “Permission-Based Access”Contracts specify required permissions:
// Single permissionrequiredPermissions: ['users:create']
// Multiple permissions (all required)requiredPermissions: ['users:create', 'admin:access']
// No permissions (authenticated only)requiredPermissions: []
// Public endpoint (no auth required)isPublic: truePermission Naming Convention
Section titled “Permission Naming Convention”<resource>:<action>
Examples:- users:read- users:create- users:update- users:delete- users:admin- orders:process- reports:generatePermission Hierarchy
Section titled “Permission Hierarchy”// Read-only operationsrequiredPermissions: ['users:read']
// Write operationsrequiredPermissions: ['users:write']
// Admin operations (includes read and write)requiredPermissions: ['users:admin']
// Super adminrequiredPermissions: ['system:admin']Public vs Private Contracts
Section titled “Public vs Private Contracts”Public Contracts
Section titled “Public Contracts”Accessible without authentication:
export const RegisterUserContract = createContract({ messageType: 'RegisterUserCommand', targetService: 'auth-service', isPublic: true, // ← No authentication required requiredPermissions: [], // Must be empty for public // ...});Use Cases:
- User registration
- Login
- Password reset
- Public data retrieval
Private Contracts
Section titled “Private Contracts”Require authentication and permissions:
export const CreateUserContract = createContract({ messageType: 'CreateUserCommand', targetService: 'user-service', isPublic: false, // ← Default, can omit requiredPermissions: ['users:create'], // ← Required permissions // ...});Contract Organization
Section titled “Contract Organization”File Structure
Section titled “File Structure”src/ contracts/ UserContracts.ts # All user-related contracts OrderContracts.ts # All order-related contracts ProductContracts.ts # All product-related contracts index.ts # Re-export all contractsGrouping Pattern
Section titled “Grouping Pattern”import { createContract } from '@banyanai/platform-contract-system';
// Commandsexport const CreateUserContract = createContract({ /* ... */ });export const UpdateUserContract = createContract({ /* ... */ });export const DeleteUserContract = createContract({ /* ... */ });
// Queriesexport const GetUserContract = createContract({ /* ... */ });export const ListUsersContract = createContract({ /* ... */ });export const SearchUsersContract = createContract({ /* ... */ });Index File
Section titled “Index File”export * from './UserContracts.js';export * from './OrderContracts.js';export * from './ProductContracts.js';Complete Example
Section titled “Complete Example”User Management Contracts
Section titled “User Management Contracts”import { createContract } from '@banyanai/platform-contract-system';
// Create Userexport const CreateUserContract = createContract({ messageType: 'CreateUserCommand', targetService: 'user-service', inputSchema: { type: 'object', properties: { email: { type: 'string', format: 'email', maxLength: 255, description: 'User email address' }, name: { type: 'string', minLength: 1, maxLength: 100, description: 'Full name' }, role: { type: 'string', enum: ['user', 'admin'], default: 'user', description: 'User role' } }, 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' } }, required: ['id', 'email', 'name', 'role', 'createdAt'] }, requiredPermissions: ['users:create']});
// Get Userexport const GetUserContract = createContract({ messageType: 'GetUserQuery', targetService: 'user-service', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'User ID' } }, required: ['id'] }, 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' } }, required: ['id', 'email', 'name', 'role', 'createdAt'] }, requiredPermissions: ['users:read']});
// List Usersexport const ListUsersContract = createContract({ messageType: 'ListUsersQuery', targetService: 'user-service', inputSchema: { type: 'object', properties: { limit: { type: 'number', minimum: 1, maximum: 100, default: 10, description: 'Number of results' }, offset: { type: 'number', minimum: 0, default: 0, description: 'Pagination offset' }, role: { type: 'string', enum: ['user', 'admin'], description: 'Filter by role' } } }, outputSchema: { type: 'object', properties: { users: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, email: { type: 'string' }, name: { type: 'string' }, role: { type: 'string' } } } }, total: { type: 'number', description: 'Total count' }, hasMore: { type: 'boolean', description: 'More results available' } }, required: ['users', 'total', 'hasMore'] }, requiredPermissions: ['users:read']});Best Practices
Section titled “Best Practices”1. Comprehensive Validation
Section titled “1. Comprehensive Validation”// Good: Detailed validationinputSchema: { type: 'object', properties: { email: { type: 'string', format: 'email', maxLength: 255 }, age: { type: 'number', minimum: 0, maximum: 150 } }, required: ['email']}
// Avoid: Weak validationinputSchema: { type: 'object', properties: { email: { type: 'string' }, // No format or length check age: { type: 'number' } // No range validation }}2. Descriptive Schemas
Section titled “2. Descriptive Schemas”// Good: Well-documentedproperties: { email: { type: 'string', format: 'email', description: 'User email address for authentication' }}
// Avoid: No documentationproperties: { email: { type: 'string' }}3. Consistent Naming
Section titled “3. Consistent Naming”// Good: Consistent across contractsCreateUserCommand, GetUserQuery, UpdateUserCommand
// Avoid: InconsistentCreateUser, FetchUserQuery, UserUpdate4. Complete Output Schemas
Section titled “4. Complete Output Schemas”// Good: Includes all relevant fieldsoutputSchema: { 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' } }}
// Avoid: Missing useful fieldsoutputSchema: { properties: { id: { type: 'string' }, email: { type: 'string' } // Missing name, role, timestamps }}5. Granular Permissions
Section titled “5. Granular Permissions”// Good: Specific permissionsrequiredPermissions: ['users:read']requiredPermissions: ['users:create']requiredPermissions: ['users:delete']
// Avoid: Overly broadrequiredPermissions: ['admin']requiredPermissions: ['all-access']Troubleshooting
Section titled “Troubleshooting”Schema Validation Errors
Section titled “Schema Validation Errors”Error: “Invalid input: email is required”
Cause: Missing required field
Solution: Include all required fields in request
Permission Denied
Section titled “Permission Denied”Error: “Forbidden: Missing required permission users:create”
Cause: User JWT lacks permission
Solution: Ensure user has permission in JWT token
Contract Not Found
Section titled “Contract Not Found”Error: “No handler registered for CreateUserCommand”
Cause: Contract not registered or service not running
Solution: Verify handler decorator and service startup
Next Steps
Section titled “Next Steps”- REST API Guide - Using contracts in REST
- GraphQL API Guide - Using contracts in GraphQL
- API Security - Authentication and authorization