Skip to content

Latest commit

 

History

History
434 lines (353 loc) · 13.1 KB

File metadata and controls

434 lines (353 loc) · 13.1 KB

AGENTS.md - GraphQL Proxy Server

🤖 Agent Instructions for GraphQL Proxy Development

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.

📋 Project Overview

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.

Core Architecture

  • 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

Key Components

  • 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

🏗️ Server Architecture Deep Dive

Operational Modes (Critical Understanding)

GETMODE (Default - Read-Only)

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

SETMODE (Offline-Capable)

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

CRUDMODE (Full Synchronization)

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

Data Flow Patterns

Query Flow (All Modes)

Client Request → Cache Check → Upstream API → Cache Storage → Client Response
                    ↓
              Cache Hit: Return cached data

Mutation Flow (SETMODE)

Client Request → Online Check → Execute Mutation → Update Local DB → Response
                    ↓
              Offline: Queue Mutation → Return queued status

Error Handling

  • Network Errors: Graceful fallback to cached data (queries only)
  • GraphQL Errors: Pass through with proper error formatting
  • Queue Failures: Logged with retry mechanisms

⚙️ Environment Configuration Mastery

Critical Environment Variables

Server Configuration

PORT=5001                    # Server listening port
CORS_ORIGIN=*               # CORS allowed origins

Upstream Connection (Required)

UPSTREAM_GRAPHQL_ENDPOINT=https://example.com/graphql  # Source GraphQL API
AUTH_TOKEN=eyJhbGci...    # JWT token for authenticated requests

Storage Paths

CACHE_DIR=./data/graphql-cache     # GraphQL response cache
QUEUE_DIR=./data/mutation-queue    # Pending mutations (SETMODE)
LOCAL_DB_PATH=./data/local-db.json # Local data store

Operational Mode

GRAPHQL_MODE=GETMODE        # GETMODE | SETMODE | CRUDMODE

Configuration Impact Matrix

Variable GETMODE SETMODE CRUDMODE Notes
UPSTREAM_GRAPHQL_ENDPOINT ✅ Required ✅ Required ✅ Required Core dependency
AUTH_TOKEN ⚠️ Optional ✅ 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

🚀 Development Workflow

Setup Commands

# 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

Testing Commands

# 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 } } }"}'

Cache Management

# 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/sync

📊 Server Response Patterns

HTTP Headers (Important for Debugging)

Cache Headers

x-cache: HIT                    # Served from cache
x-cache: MISS                   # Fetched from upstream
x-cache-key: abc123...         # Cache key hash

Mode Headers

x-mode: GETMODE                 # Current operational mode
x-mutation: executed           # Mutation status
x-queue-id: 123456789-abc      # Queue ID for offline mutations

Status Headers

x-upstream-status: 200          # Upstream response status
x-local-synced: true           # Local DB updated

Response Codes

  • 200: Success (including cached responses)
  • 400: Bad request (malformed GraphQL)
  • 500: Server error

GraphQL Error Extensions

{
  "errors": [{
    "message": "Mutations are disabled in GETMODE",
    "extensions": {
      "code": "MUTATION_BLOCKED"
    }
  }]
}

🔧 Code Patterns and Conventions

Server Structure

// 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

Utility Functions

// 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 || '');
}

Error Handling

// 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,
    },
  });
}

🔒 Security Considerations

Authentication

  • JWT Tokens: Bearer token authentication for upstream requests
  • CORS Policy: Configurable origin restrictions
  • Input Validation: GraphQL queries are not dynamically constructed

Data Protection

  • No Sensitive Data Storage: Cache contains only GraphQL responses
  • Queue Encryption: Consider encrypting queued mutations if sensitive
  • HTTPS Only: Always use HTTPS in production

Rate Limiting

  • Currently not implemented (consider adding for production)
  • Monitor request patterns for abuse detection

🐛 Debugging and Troubleshooting

Common Issues

"UPSTREAM_GRAPHQL_ENDPOINT is required"

  • Check environment variable spelling
  • Verify URL format and accessibility
  • Test upstream endpoint directly

Cache Not Working

  • Check CACHE_DIR permissions
  • Verify directory exists: mkdir -p data/graphql-cache
  • Test with simple query first

Mutations Not Queued (SETMODE)

  • Verify GRAPHQL_MODE=SETMODE
  • Check QUEUE_DIR permissions
  • Test upstream connectivity

CORS Errors

  • Check CORS_ORIGIN setting
  • Verify client domain matches
  • Test with CORS_ORIGIN=* for debugging

Debug Headers

# Enable verbose output
curl -v -X POST http://localhost:5001/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ posts(first: 1) { nodes { id } } }"}'

Log Analysis

# Server logs show:
# - Startup configuration
# - Cache hits/misses
# - Queue operations
# - Upstream request failures

📚 Documentation Navigation

Quick Reference

docs/
├── 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

Entity Relationships

  • 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

GraphQL Schema Knowledge

  • 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)

🎯 Implementation Guidelines

When Adding Features

For New Endpoints

  1. Add route handler in server structure
  2. Include CORS headers
  3. Add proper error handling
  4. Update API documentation
  5. Test with all modes

For Environment Variables

  1. Add to env.example with clear description
  2. Update server configuration logic
  3. Add validation if required
  4. Update documentation
  5. Test default and custom values

For Mode-Specific Logic

  1. Always check GRAPHQL_MODE first
  2. Implement mode-specific behavior
  3. Add appropriate headers
  4. Handle offline scenarios
  5. Test all three modes

Code Quality Standards

  • 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

Testing Checklist

  • 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)

🚀 Deployment Considerations

Production Checklist

  • 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

Docker Deployment

# 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

Scaling Considerations

  • 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)

📞 Support and Resources

Documentation Links

Key Files to Understand

  • server/index.ts - Main server implementation
  • env.example - All configuration options
  • docs/guides/ - Integration examples
  • package.json - Dependencies and scripts

Community Resources

  • 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!