oRPC Extension

JSON-RPC 2.0 server with OpenRPC schema and automatic REST-to-RPC conversion

oRPC Extension

The oRPC (Open RPC) extension automatically exposes your Forge application's REST API as JSON-RPC 2.0 methods with OpenRPC schema support. This provides a unified RPC interface for your existing HTTP endpoints with full specification compliance.

Features

JSON-RPC 2.0 Compliance

  • Full Specification Support: 100% compliant with JSON-RPC 2.0 specification
  • Request/Response Handling: Proper request validation and response formatting
  • Batch Requests: Support for batch request processing with configurable limits
  • Notification Support: Handle notification requests (requests without ID)
  • Error Handling: Standard JSON-RPC error codes and custom error responses

OpenRPC Schema Generation

  • Automatic Schema: Generates OpenRPC 1.3.2 compliant schema documentation
  • Type Safety: Schema validation and type hints for parameters and results
  • Method Discovery: Runtime method discovery and introspection
  • Custom Annotations: Add custom parameter and result schemas via route options
  • Documentation: Rich method descriptions and examples

REST-to-RPC Conversion

  • Automatic Method Generation: Converts REST routes to JSON-RPC 2.0 methods
  • Route Execution: Executes underlying HTTP endpoints via JSON-RPC calls
  • Flexible Naming: Multiple naming strategies (path-based, method-based, custom)
  • Pattern Matching: Include/exclude routes with glob patterns
  • Custom Method Names: Override auto-generated method names

Advanced Features

  • Interceptors: Add middleware for authentication, logging, and metrics
  • Rate Limiting: Built-in rate limiting with configurable limits
  • Request Size Limits: Prevent memory exhaustion with size controls
  • Caching: Schema caching for improved performance
  • Observability: Integrated metrics, logging, and tracing

Installation

Go Module

go get github.com/xraph/forge/extensions/orpc

Docker

FROM xraph/forge:latest
# oRPC extension is included

Package Manager

# Using Forge CLI
forge extension add orpc

# Using package manager
npm install @xraph/forge-orpc

Configuration

YAML Configuration

extensions:
  orpc:
    # Core settings
    enabled: true
    endpoint: "/rpc"
    openrpc_endpoint: "/rpc/schema"
    
    # Server information
    server_name: "My API"
    server_version: "1.0.0"
    
    # Auto-exposure settings
    auto_expose_routes: true
    method_prefix: "api."
    exclude_patterns:
      - "/_/*"          # Exclude health/metrics endpoints
      - "/internal/*"   # Exclude internal routes
      - "/debug/*"      # Exclude debug routes
    include_patterns: []  # If set, only expose matching patterns
    
    # Features
    enable_openrpc: true
    enable_discovery: true
    enable_batch: true
    batch_limit: 10
    
    # Naming strategy: "path", "method", or "custom"
    naming_strategy: "path"
    
    # Security & Performance
    max_request_size: 1048576  # 1MB
    request_timeout: 30        # seconds
    schema_cache: true
    enable_metrics: true
    
    # Authentication (optional)
    auth:
      header: "X-API-Key"
      tokens:
        - "secret-token-1"
        - "secret-token-2"
    
    # Rate limiting (optional)
    rate_limit:
      requests_per_minute: 100
      burst: 10

Environment Variables

# Core Configuration
FORGE_ORPC_ENABLED=true
FORGE_ORPC_ENDPOINT=/rpc
FORGE_ORPC_OPENRPC_ENDPOINT=/rpc/schema

# Server Information
FORGE_ORPC_SERVER_NAME="My API"
FORGE_ORPC_SERVER_VERSION=1.0.0

# Auto-exposure
FORGE_ORPC_AUTO_EXPOSE_ROUTES=true
FORGE_ORPC_METHOD_PREFIX=api.
FORGE_ORPC_EXCLUDE_PATTERNS="/_/*,/internal/*"

# Features
FORGE_ORPC_ENABLE_OPENRPC=true
FORGE_ORPC_ENABLE_DISCOVERY=true
FORGE_ORPC_ENABLE_BATCH=true
FORGE_ORPC_BATCH_LIMIT=10

# Performance
FORGE_ORPC_MAX_REQUEST_SIZE=1048576
FORGE_ORPC_REQUEST_TIMEOUT=30
FORGE_ORPC_SCHEMA_CACHE=true

Programmatic Configuration

package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/orpc"
)

func main() {
    app := forge.NewApp(forge.AppConfig{
        Name:    "my-api",
        Version: "1.0.0",
    })

    // Configure oRPC extension
    app.RegisterExtension(orpc.NewExtension(
        orpc.WithEnabled(true),
        orpc.WithEndpoint("/rpc"),
        orpc.WithOpenRPCEndpoint("/rpc/schema"),
        orpc.WithServerInfo("My API", "1.0.0"),
        orpc.WithAutoExposeRoutes(true),
        orpc.WithMethodPrefix("api."),
        orpc.WithExcludePatterns([]string{"/_/*", "/internal/*"}),
        orpc.WithIncludePatterns([]string{"/api/*"}), // Only expose /api/* routes
        orpc.WithOpenRPC(true),
        orpc.WithDiscovery(true),
        orpc.WithBatch(true),
        orpc.WithBatchLimit(10),
        orpc.WithNamingStrategy("path"),
        orpc.WithMaxRequestSize(1024 * 1024), // 1MB
        orpc.WithRequestTimeout(30),          // seconds
        orpc.WithSchemaCache(true),
        orpc.WithMetrics(true),
    ))

    app.Start()
}

Usage Examples

Basic REST API with Auto-Exposure

package main

import (
    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/orpc"
)

type User struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

type CreateUserRequest struct {
    Name  string `json:"name" validate:"required"`
    Email string `json:"email" validate:"required,email"`
}

func main() {
    app := forge.NewApp(forge.AppConfig{
        Name:    "user-api",
        Version: "1.0.0",
    })

    // Add REST routes
    app.Router().GET("/users/:id", getUserHandler,
        forge.WithSummary("Get user by ID"),
        forge.WithDescription("Retrieve a user by their unique identifier"),
        forge.WithTags("users"),
        forge.WithORPCMethod("user.get"), // Custom JSON-RPC method name
        forge.WithORPCParams(&orpc.ParamsSchema{
            Type: "object",
            Properties: map[string]*orpc.PropertySchema{
                "id": {
                    Type:        "string",
                    Description: "User ID",
                    Example:     "user-123",
                },
            },
            Required: []string{"id"},
        }),
        forge.WithORPCResult(&orpc.ResultSchema{
            Type:        "object",
            Description: "User object",
            Properties: map[string]*orpc.PropertySchema{
                "id":    {Type: "string", Description: "User ID"},
                "name":  {Type: "string", Description: "User name"},
                "email": {Type: "string", Description: "User email"},
            },
        }),
    )

    app.Router().POST("/users", createUserHandler,
        forge.WithSummary("Create new user"),
        forge.WithDescription("Create a new user account"),
        forge.WithTags("users"),
        forge.WithORPCMethod("user.create"),
        forge.WithRequestBody(CreateUserRequest{}, "User creation data"),
        forge.WithResponse(201, User{}, "Created user"),
    )

    app.Router().PUT("/users/:id", updateUserHandler,
        forge.WithSummary("Update user"),
        forge.WithTags("users"),
        forge.WithORPCMethod("user.update"),
    )

    app.Router().DELETE("/users/:id", deleteUserHandler,
        forge.WithSummary("Delete user"),
        forge.WithTags("users"),
        forge.WithORPCMethod("user.delete"),
    )

    // Exclude internal routes from oRPC exposure
    app.Router().GET("/_/health", healthHandler,
        forge.WithORPCExclude(), // Don't expose as JSON-RPC method
    )

    // Enable oRPC with auto-exposure
    app.RegisterExtension(orpc.NewExtension(
        orpc.WithEnabled(true),
        orpc.WithAutoExposeRoutes(true),
        orpc.WithMethodPrefix("api."),
        orpc.WithExcludePatterns([]string{"/_/*"}),
    ))

    app.Start()
}

func getUserHandler(ctx forge.Context) error {
    userID := ctx.Param("id")
    
    // Simulate user lookup
    user := User{
        ID:    userID,
        Name:  "John Doe",
        Email: "john@example.com",
    }
    
    return ctx.JSON(200, user)
}

func createUserHandler(ctx forge.Context) error {
    var req CreateUserRequest
    if err := ctx.Bind(&req); err != nil {
        return ctx.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Simulate user creation
    user := User{
        ID:    "user-" + generateID(),
        Name:  req.Name,
        Email: req.Email,
    }
    
    return ctx.JSON(201, user)
}

func updateUserHandler(ctx forge.Context) error {
    userID := ctx.Param("id")
    
    var req CreateUserRequest
    if err := ctx.Bind(&req); err != nil {
        return ctx.JSON(400, map[string]string{"error": "Invalid request"})
    }
    
    // Simulate user update
    user := User{
        ID:    userID,
        Name:  req.Name,
        Email: req.Email,
    }
    
    return ctx.JSON(200, user)
}

func deleteUserHandler(ctx forge.Context) error {
    userID := ctx.Param("id")
    
    // Simulate user deletion
    return ctx.JSON(200, map[string]string{
        "message": "User deleted",
        "id":      userID,
    })
}

This automatically creates JSON-RPC methods:

  • api.user.getGET /users/:id
  • api.user.createPOST /users
  • api.user.updatePUT /users/:id
  • api.user.deleteDELETE /users/:id

Manual Method Registration

package main

import (
    "context"
    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/orpc"
)

func main() {
    app := forge.NewApp(forge.AppConfig{
        Name:    "calculator-api",
        Version: "1.0.0",
    })

    // Register oRPC extension
    app.RegisterExtension(orpc.NewExtension(
        orpc.WithEnabled(true),
        orpc.WithAutoExposeRoutes(false), // Disable auto-exposure
    ))

    app.Start()

    // Get oRPC server from DI container
    orpcServer := forge.Must[orpc.ORPC](app.Container(), "orpc")

    // Register custom methods
    orpcServer.RegisterMethod(&orpc.Method{
        Name:        "math.add",
        Description: "Add two numbers",
        Params: &orpc.ParamsSchema{
            Type: "object",
            Properties: map[string]*orpc.PropertySchema{
                "a": {Type: "number", Description: "First number"},
                "b": {Type: "number", Description: "Second number"},
            },
            Required: []string{"a", "b"},
        },
        Result: &orpc.ResultSchema{
            Type:        "number",
            Description: "Sum of a and b",
        },
        Handler: func(ctx context.Context, params json.RawMessage) (interface{}, error) {
            var args struct {
                A float64 `json:"a"`
                B float64 `json:"b"`
            }
            
            if err := json.Unmarshal(params, &args); err != nil {
                return nil, orpc.NewError(orpc.ErrInvalidParams, "Invalid parameters", nil)
            }
            
            return args.A + args.B, nil
        },
        Tags: []string{"math", "arithmetic"},
    })

    orpcServer.RegisterMethod(&orpc.Method{
        Name:        "math.multiply",
        Description: "Multiply two numbers",
        Params: &orpc.ParamsSchema{
            Type: "object",
            Properties: map[string]*orpc.PropertySchema{
                "a": {Type: "number", Description: "First number"},
                "b": {Type: "number", Description: "Second number"},
            },
            Required: []string{"a", "b"},
        },
        Result: &orpc.ResultSchema{
            Type:        "number",
            Description: "Product of a and b",
        },
        Handler: func(ctx context.Context, params json.RawMessage) (interface{}, error) {
            var args struct {
                A float64 `json:"a"`
                B float64 `json:"b"`
            }
            
            if err := json.Unmarshal(params, &args); err != nil {
                return nil, orpc.NewError(orpc.ErrInvalidParams, "Invalid parameters", nil)
            }
            
            return args.A * args.B, nil
        },
        Tags: []string{"math", "arithmetic"},
    })

    app.Run()
}

Client Usage Examples

Single Request

# Using curl
curl -X POST http://localhost:8080/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "api.user.get",
    "params": {"id": "user-123"},
    "id": 1
  }'

# Response
{
  "jsonrpc": "2.0",
  "result": {
    "id": "user-123",
    "name": "John Doe",
    "email": "john@example.com"
  },
  "id": 1
}

Batch Request

curl -X POST http://localhost:8080/rpc \
  -H "Content-Type: application/json" \
  -d '[
    {
      "jsonrpc": "2.0",
      "method": "api.user.get",
      "params": {"id": "user-123"},
      "id": 1
    },
    {
      "jsonrpc": "2.0",
      "method": "api.user.create",
      "params": {
        "name": "Jane Doe",
        "email": "jane@example.com"
      },
      "id": 2
    }
  ]'

# Response
[
  {
    "jsonrpc": "2.0",
    "result": {
      "id": "user-123",
      "name": "John Doe",
      "email": "john@example.com"
    },
    "id": 1
  },
  {
    "jsonrpc": "2.0",
    "result": {
      "id": "user-456",
      "name": "Jane Doe",
      "email": "jane@example.com"
    },
    "id": 2
  }
]

Notification (No Response)

curl -X POST http://localhost:8080/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "api.user.delete",
    "params": {"id": "user-123"}
  }'

# No response (notification)

JavaScript Client

class ORPCClient {
    constructor(endpoint) {
        this.endpoint = endpoint;
        this.id = 1;
    }

    async call(method, params) {
        const request = {
            jsonrpc: "2.0",
            method: method,
            params: params,
            id: this.id++
        };

        const response = await fetch(this.endpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(request)
        });

        const result = await response.json();
        
        if (result.error) {
            throw new Error(`RPC Error ${result.error.code}: ${result.error.message}`);
        }
        
        return result.result;
    }

    async batch(calls) {
        const requests = calls.map(([method, params]) => ({
            jsonrpc: "2.0",
            method: method,
            params: params,
            id: this.id++
        }));

        const response = await fetch(this.endpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(requests)
        });

        return await response.json();
    }

    notify(method, params) {
        const request = {
            jsonrpc: "2.0",
            method: method,
            params: params
        };

        return fetch(this.endpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(request)
        });
    }
}

// Usage
const client = new ORPCClient('http://localhost:8080/rpc');

// Single call
const user = await client.call('api.user.get', { id: 'user-123' });
console.log(user);

// Batch call
const results = await client.batch([
    ['api.user.get', { id: 'user-123' }],
    ['api.user.create', { name: 'Jane Doe', email: 'jane@example.com' }]
]);
console.log(results);

// Notification
client.notify('api.user.delete', { id: 'user-123' });

Advanced Features

Custom Interceptors

package main

import (
    "context"
    "strings"
    "time"
    
    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/orpc"
)

// AuthInterceptor validates API keys
func AuthInterceptor(validTokens []string) orpc.Interceptor {
    return func(ctx context.Context, req *orpc.Request, next orpc.Handler) *orpc.Response {
        // Skip auth for discovery methods
        if strings.HasPrefix(req.Method, "rpc.") {
            return next(ctx, req)
        }
        
        // Check for API key in context (from HTTP header)
        apiKey := ctx.Value("api_key")
        if apiKey == nil {
            return orpc.NewErrorResponse(req.ID, orpc.ErrUnauthorized, "Missing API key", nil)
        }
        
        // Validate API key
        valid := false
        for _, token := range validTokens {
            if apiKey == token {
                valid = true
                break
            }
        }
        
        if !valid {
            return orpc.NewErrorResponse(req.ID, orpc.ErrUnauthorized, "Invalid API key", nil)
        }
        
        return next(ctx, req)
    }
}

// LoggingInterceptor logs all RPC calls
func LoggingInterceptor(logger forge.Logger) orpc.Interceptor {
    return func(ctx context.Context, req *orpc.Request, next orpc.Handler) *orpc.Response {
        start := time.Now()
        
        logger.Info("rpc call started",
            forge.F("method", req.Method),
            forge.F("id", req.ID),
            forge.F("has_params", req.Params != nil),
        )
        
        resp := next(ctx, req)
        
        duration := time.Since(start)
        
        if resp.Error != nil {
            logger.Error("rpc call failed",
                forge.F("method", req.Method),
                forge.F("id", req.ID),
                forge.F("error_code", resp.Error.Code),
                forge.F("error_message", resp.Error.Message),
                forge.F("duration", duration),
            )
        } else {
            logger.Info("rpc call completed",
                forge.F("method", req.Method),
                forge.F("id", req.ID),
                forge.F("duration", duration),
            )
        }
        
        return resp
    }
}

// MetricsInterceptor records metrics
func MetricsInterceptor(metrics forge.Metrics) orpc.Interceptor {
    return func(ctx context.Context, req *orpc.Request, next orpc.Handler) *orpc.Response {
        start := time.Now()
        
        metrics.Counter("orpc_requests_total").
            WithLabels(forge.Labels{"method": req.Method}).
            Inc()
        
        resp := next(ctx, req)
        
        duration := time.Since(start)
        
        status := "success"
        if resp.Error != nil {
            status = "error"
        }
        
        metrics.Counter("orpc_requests_completed_total").
            WithLabels(forge.Labels{
                "method": req.Method,
                "status": status,
            }).
            Inc()
        
        metrics.Histogram("orpc_request_duration_seconds").
            WithLabels(forge.Labels{"method": req.Method}).
            Observe(duration.Seconds())
        
        return resp
    }
}

func main() {
    app := forge.NewApp(forge.AppConfig{
        Name:    "secure-api",
        Version: "1.0.0",
    })

    // Register oRPC extension
    app.RegisterExtension(orpc.NewExtension(
        orpc.WithEnabled(true),
        orpc.WithAutoExposeRoutes(true),
    ))

    app.Start()

    // Get oRPC server and add interceptors
    orpcServer := forge.Must[orpc.ORPC](app.Container(), "orpc")
    logger := forge.Must[forge.Logger](app.Container(), "logger")
    metrics := forge.Must[forge.Metrics](app.Container(), "metrics")

    // Add interceptors (order matters - they wrap each other)
    orpcServer.Use(LoggingInterceptor(logger))
    orpcServer.Use(MetricsInterceptor(metrics))
    orpcServer.Use(AuthInterceptor([]string{"secret-key-1", "secret-key-2"}))

    app.Run()
}

Schema Validation

package main

import (
    "context"
    "encoding/json"
    "fmt"
    
    "github.com/xraph/forge"
    "github.com/xraph/forge/extensions/orpc"
    "github.com/go-playground/validator/v10"
)

type UserService struct {
    validator *validator.Validate
}

func NewUserService() *UserService {
    return &UserService{
        validator: validator.New(),
    }
}

func (s *UserService) RegisterMethods(orpcServer orpc.ORPC) {
    // Register user.create method with validation
    orpcServer.RegisterMethod(&orpc.Method{
        Name:        "user.create",
        Description: "Create a new user with validation",
        Params: &orpc.ParamsSchema{
            Type: "object",
            Properties: map[string]*orpc.PropertySchema{
                "name": {
                    Type:        "string",
                    Description: "User's full name",
                    MinLength:   &[]int{2}[0],
                    MaxLength:   &[]int{100}[0],
                },
                "email": {
                    Type:        "string",
                    Description: "User's email address",
                    Format:      "email",
                },
                "age": {
                    Type:        "integer",
                    Description: "User's age",
                    Minimum:     &[]float64{18}[0],
                    Maximum:     &[]float64{120}[0],
                },
            },
            Required: []string{"name", "email", "age"},
        },
        Result: &orpc.ResultSchema{
            Type:        "object",
            Description: "Created user",
            Properties: map[string]*orpc.PropertySchema{
                "id":    {Type: "string", Description: "User ID"},
                "name":  {Type: "string", Description: "User name"},
                "email": {Type: "string", Description: "User email"},
                "age":   {Type: "integer", Description: "User age"},
            },
        },
        Handler: s.createUser,
        Tags:    []string{"users", "crud"},
    })
}

func (s *UserService) createUser(ctx context.Context, params json.RawMessage) (interface{}, error) {
    var req struct {
        Name  string `json:"name" validate:"required,min=2,max=100"`
        Email string `json:"email" validate:"required,email"`
        Age   int    `json:"age" validate:"required,min=18,max=120"`
    }
    
    // Parse parameters
    if err := json.Unmarshal(params, &req); err != nil {
        return nil, orpc.NewError(orpc.ErrInvalidParams, "Invalid JSON parameters", nil)
    }
    
    // Validate parameters
    if err := s.validator.Struct(req); err != nil {
        var validationErrors []string
        for _, err := range err.(validator.ValidationErrors) {
            validationErrors = append(validationErrors, fmt.Sprintf(
                "Field '%s' failed validation: %s", 
                err.Field(), 
                err.Tag(),
            ))
        }
        
        return nil, orpc.NewError(orpc.ErrInvalidParams, "Validation failed", map[string]interface{}{
            "errors": validationErrors,
        })
    }
    
    // Simulate user creation
    user := map[string]interface{}{
        "id":    fmt.Sprintf("user-%d", time.Now().Unix()),
        "name":  req.Name,
        "email": req.Email,
        "age":   req.Age,
    }
    
    return user, nil
}

OpenRPC Schema Access

# Get OpenRPC schema
curl http://localhost:8080/rpc/schema

# Response
{
  "openrpc": "1.3.2",
  "info": {
    "title": "My API",
    "version": "1.0.0",
    "description": "JSON-RPC 2.0 API"
  },
  "methods": [
    {
      "name": "api.user.get",
      "description": "Get user by ID",
      "params": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "User ID"
          }
        },
        "required": ["id"]
      },
      "result": {
        "type": "object",
        "description": "User object",
        "properties": {
          "id": {"type": "string"},
          "name": {"type": "string"},
          "email": {"type": "string"}
        }
      },
      "tags": ["users"]
    }
  ]
}

# List available methods
curl http://localhost:8080/rpc/methods

# Response
{
  "methods": [
    "api.user.get",
    "api.user.create",
    "api.user.update",
    "api.user.delete"
  ]
}

Best Practices

Method Design

  • Use semantic naming: Choose descriptive method names that clearly indicate functionality
  • Follow conventions: Use dot notation for namespacing (e.g., user.create, order.list)
  • Version your methods: Include version in method names for breaking changes
  • Document thoroughly: Provide clear descriptions and examples

Parameter Validation

  • Validate all inputs: Use schema validation and custom validators
  • Provide clear errors: Return meaningful error messages with validation details
  • Use appropriate types: Choose correct JSON schema types for parameters
  • Set reasonable limits: Define min/max values and string lengths

Error Handling

  • Use standard codes: Follow JSON-RPC 2.0 error code conventions
  • Provide context: Include helpful error details and suggestions
  • Log errors properly: Log errors with sufficient context for debugging
  • Handle edge cases: Consider null values, empty arrays, and invalid types

Performance Optimization

  • Enable caching: Use schema caching for better performance
  • Set request limits: Configure appropriate request size and batch limits
  • Use interceptors wisely: Avoid heavy processing in interceptors
  • Monitor metrics: Track request rates, durations, and error rates

Security Considerations

  • Implement authentication: Validate API keys or tokens
  • Use rate limiting: Prevent abuse with rate limiting
  • Validate input size: Set maximum request sizes
  • Sanitize parameters: Validate and sanitize all input data

Troubleshooting

Common Issues

Method Not Found

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32601,
    "message": "Method not found"
  },
  "id": 1
}

Solutions:

  • Check method name spelling
  • Verify auto-exposure patterns
  • Ensure route is registered before oRPC starts
  • Check exclude/include patterns

Invalid Parameters

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "errors": ["Field 'email' failed validation: email"]
    }
  },
  "id": 1
}

Solutions:

  • Validate parameter schema
  • Check required fields
  • Verify parameter types
  • Review validation rules

Batch Too Large

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "Batch size exceeds limit"
  },
  "id": null
}

Solutions:

  • Reduce batch size
  • Increase batch_limit configuration
  • Split large batches into smaller ones

Debugging

Enable Debug Logging

logging:
  level: debug
  loggers:
    orpc: debug
    orpc.server: debug
    orpc.schema: debug

Monitor Metrics

// Check server statistics
stats := orpcServer.GetStats()
fmt.Printf("Total Requests: %d\n", stats.TotalRequests)
fmt.Printf("Total Errors: %d\n", stats.TotalErrors)
fmt.Printf("Average Duration: %v\n", stats.AverageDuration)

Inspect Schema

# Get schema for debugging
curl http://localhost:8080/rpc/schema | jq .

# List all methods
curl http://localhost:8080/rpc/methods | jq .

Next Steps

How is this guide?

Last updated on