This AGENTS.md provides comprehensive guidance for AI coding agents working on the GraphQL proxy server. It covers project structure, server functionality, environment configuration, and development workflows to help you make informed decisions and implement features correctly.
GraphQL Proxy Server - A high-performance proxy server built with Elysia.js (Bun runtime) that sits between GraphQL clients and upstream GraphQL APIs. Provides caching, offline mutation queuing, and local data synchronization.
- Runtime: Bun (JavaScript/TypeScript)
- Framework: Elysia.js (web framework)
- Primary Function: GraphQL proxy with three operational modes
- Data Flow: Client → Proxy → Upstream GraphQL API → Response with caching
- GraphQL Proxy Endpoint:
/graphql- Main proxy interface - Health Check:
/health- Server status and configuration - Local Data Management:
/local/*- Sync and data access - Queue Management:
/flush,/queue- Mutation queue handling - Caching System: File-based response caching
- Local Database: JSON-based offline data store
GRAPHQL_MODE=GETMODE- Queries: Forward to upstream, cache responses indefinitely
- Mutations: Strictly blocked with error
MUTATION_BLOCKED - Offline: Serve cached responses when upstream unavailable
- Use Case: Public blogs, documentation sites, read-only apps
GRAPHQL_MODE=SETMODE- Queries: Same as GETMODE
- Mutations: Execute online, queue offline, always sync to local DB
- Offline: Queue mutations, sync when connection restored
- Use Case: CMS applications, mobile apps with offline editing
GRAPHQL_MODE=CRUDMODE- Queries: Same as GETMODE
- Mutations: Execute online only, fail when upstream unavailable
- Offline: Reject all mutations with
UPSTREAM_UNAVAILABLE - Use Case: Real-time collaborative editing, strict online requirements
Client Request → Cache Check → Upstream API → Cache Storage → Client Response
↓
Cache Hit: Return cached data
Client Request → Online Check → Execute Mutation → Update Local DB → Response
↓
Offline: Queue Mutation → Return queued status
- Network Errors: Graceful fallback to cached data (queries only)
- GraphQL Errors: Pass through with proper error formatting
- Queue Failures: Logged with retry mechanisms
PORT=5001 # Server listening port
CORS_ORIGIN=* # CORS allowed originsUPSTREAM_GRAPHQL_ENDPOINT=https://example.com/graphql # Source GraphQL API
AUTH_TOKEN=eyJhbGci... # JWT token for authenticated requestsCACHE_DIR=./data/graphql-cache # GraphQL response cache
QUEUE_DIR=./data/mutation-queue # Pending mutations (SETMODE)
LOCAL_DB_PATH=./data/local-db.json # Local data storeGRAPHQL_MODE=GETMODE # GETMODE | SETMODE | CRUDMODE| Variable | GETMODE | SETMODE | CRUDMODE | Notes |
|---|---|---|---|---|
UPSTREAM_GRAPHQL_ENDPOINT |
✅ Required | ✅ Required | ✅ Required | Core dependency |
AUTH_TOKEN |
✅ Recommended | ✅ Recommended | For authenticated requests | |
CACHE_DIR |
✅ Used | ✅ Used | ✅ Used | Response caching |
QUEUE_DIR |
❌ N/A | ✅ Required | ❌ N/A | Mutation queuing |
LOCAL_DB_PATH |
❌ N/A | ✅ Required | ❌ N/A | Local data sync |
# Install dependencies
bun install
# Start development server
bun run dev
# Start production server
bun run start
# Run with specific environment
GRAPHQL_MODE=SETMODE bun run start# Health check
curl http://localhost:5001/health
# Test GraphQL query
curl -X POST http://localhost:5001/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ posts(first: 1) { nodes { id title } } }"}'
# Test caching (run query twice)
curl -X POST http://localhost:5001/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ posts(first: 1) { nodes { id title } } }"}' \
-v # Check x-cache header
# Test mutations (SETMODE only)
curl -X POST http://localhost:5001/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-d '{"query": "mutation { createPost(input: { title: \"Test\" }) { post { id } } }"}'# Clear all caches
rm -rf data/graphql-cache/*
# Clear mutation queue
rm -rf data/mutation-queue/*
# Reset local database
echo '{"posts":[],"categories":[],"tags":[],"authors":[],"pages":[]}' > data/local-db.json
# Force data sync
curl -X POST http://localhost:5001/local/syncx-cache: HIT # Served from cache
x-cache: MISS # Fetched from upstream
x-cache-key: abc123... # Cache key hash
x-mode: GETMODE # Current operational mode
x-mutation: executed # Mutation status
x-queue-id: 123456789-abc # Queue ID for offline mutations
x-upstream-status: 200 # Upstream response status
x-local-synced: true # Local DB updated
200: Success (including cached responses)400: Bad request (malformed GraphQL)500: Server error
{
"errors": [{
"message": "Mutations are disabled in GETMODE",
"extensions": {
"code": "MUTATION_BLOCKED"
}
}]
}// Main app setup
const app = new Elysia()
.options('/*', corsHandler) // CORS preflight
.get('/health', healthHandler) // Health endpoint
.post('/graphql', graphqlHandler) // Main proxy
.get('/local/data', localDataHandler) // Local data access
.post('/local/sync', syncHandler) // Data synchronization
.post('/flush', flushHandler) // Queue processing
.get('/queue', queueHandler) // Queue inspection// Cache key generation
function cacheKey(payload: GraphQLRequest): string {
const query = payload.query || '';
const variables = JSON.stringify(payload.variables || {});
return createHash('sha256').update(query + variables).digest('hex');
}
// Mutation detection
function isMutation(query?: string): boolean {
return query?.trim().startsWith('mutation') ||
/^\s*mutation\s/i.test(query || '');
}// Consistent error responses
function jsonResponse(body: unknown, status = 200, extraHeaders?: Record<string, string>) {
return new Response(JSON.stringify(body), {
status,
headers: {
'content-type': 'application/json; charset=utf-8',
'access-control-allow-origin': CORS_ORIGIN,
...extraHeaders,
},
});
}- JWT Tokens: Bearer token authentication for upstream requests
- CORS Policy: Configurable origin restrictions
- Input Validation: GraphQL queries are not dynamically constructed
- No Sensitive Data Storage: Cache contains only GraphQL responses
- Queue Encryption: Consider encrypting queued mutations if sensitive
- HTTPS Only: Always use HTTPS in production
- Currently not implemented (consider adding for production)
- Monitor request patterns for abuse detection
- Check environment variable spelling
- Verify URL format and accessibility
- Test upstream endpoint directly
- Check
CACHE_DIRpermissions - Verify directory exists:
mkdir -p data/graphql-cache - Test with simple query first
- Verify
GRAPHQL_MODE=SETMODE - Check
QUEUE_DIRpermissions - Test upstream connectivity
- Check
CORS_ORIGINsetting - Verify client domain matches
- Test with
CORS_ORIGIN=*for debugging
# Enable verbose output
curl -v -X POST http://localhost:5001/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ posts(first: 1) { nodes { id } } }"}'# Server logs show:
# - Startup configuration
# - Cache hits/misses
# - Queue operations
# - Upstream request failuresdocs/
├── getting-started/index.md # Quick setup guide
├── api/server-api.md # Complete API reference
├── configuration/ # Environment variables
├── deployment/docker.md # Production deployment
├── guides/ # Application integration guides
│ ├── cms-integration-101.md # Full CRUD CMS example
│ ├── getmode-app-integration-101.md # Read-only app example
│ └── wordpress-graphql-setup-101.md # Backend setup guide
└── troubleshooting/ # Common issues and solutions
- Posts: Main content entity with categories, tags, author
- Categories: Hierarchical taxonomy for posts
- Tags: Flat taxonomy for posts
- Authors: User entities
- Pages: Static content entities
- WordPress GraphQL API: Standard WPGraphQL schema
- Post Fields: id, title, content, excerpt, slug, date, status
- Connection Types: posts, categories, tags with Relay pagination
- Mutations: create/update/delete operations (when authenticated)
- Add route handler in server structure
- Include CORS headers
- Add proper error handling
- Update API documentation
- Test with all modes
- Add to
env.examplewith clear description - Update server configuration logic
- Add validation if required
- Update documentation
- Test default and custom values
- Always check
GRAPHQL_MODEfirst - Implement mode-specific behavior
- Add appropriate headers
- Handle offline scenarios
- Test all three modes
- TypeScript Strict: All types properly defined
- Error Handling: Comprehensive try-catch blocks
- Logging: Appropriate console logging
- Documentation: Inline comments for complex logic
- Security: Input validation and sanitization
- All three modes tested
- Online and offline scenarios covered
- Cache functionality verified
- Queue operations tested (SETMODE)
- Error conditions handled
- CORS configuration tested
- Authentication tested (if applicable)
- Environment variables properly set
- HTTPS enabled
- CORS configured for production domains
- Monitoring and logging configured
- Cache and queue directories have proper permissions
- Upstream endpoint is stable and monitored
# Build and run
docker build -t graphql-proxy .
docker run -d \
--name graphql-proxy \
-p 5001:5001 \
-e UPSTREAM_GRAPHQL_ENDPOINT=https://api.example.com/graphql \
-e GRAPHQL_MODE=SETMODE \
-v ./data:/app/data \
graphql-proxy- Horizontal Scaling: Stateless design supports multiple instances
- Load Balancing: Can be placed behind reverse proxy
- Cache Strategy: Shared cache volume for multiple instances
- Queue Handling: Single instance for queue processing (or external queue system)
server/index.ts- Main server implementationenv.example- All configuration optionsdocs/guides/- Integration examplespackage.json- Dependencies and scripts
- Elysia.js documentation
- WPGraphQL documentation
- GraphQL specification
Remember: This server is a specialized GraphQL proxy. Always consider the operational mode and data flow patterns when implementing changes. The three modes (GETMODE/SETMODE/CRUDMODE) fundamentally change behavior - test thoroughly!