API Documentation Integration

Best practices for documenting your JSON-RPC APIs using industry-standard tools

Overview

Properly documenting JSON-RPC APIs is crucial for developer adoption and ease of use. While JSON-RPC doesn't have the same level of built-in documentation support as REST APIs, there are effective strategies for integrating JSON-RPC with popular documentation systems.

This guide explores various approaches to documenting JSON-RPC APIs, from adapting existing tools like OpenAPI/Swagger to creating custom documentation solutions specifically designed for JSON-RPC's unique characteristics.

JSON-RPC Documentation Challenges

Before exploring solutions, it's important to understand why documenting JSON-RPC APIs presents unique challenges:

Single Endpoint

Unlike REST APIs with many endpoints, JSON-RPC typically uses a single endpoint for all methods.

Method-Based Design

JSON-RPC focuses on methods rather than resources, which doesn't align with REST-oriented documentation tools.

Complex Parameter Structures

JSON-RPC often uses complex nested objects as parameters, which can be difficult to document clearly.

Batch Requests

Documenting batch request/response patterns doesn't fit well in traditional API documentation approaches.

OpenAPI Integration

Despite being designed primarily for REST APIs, OpenAPI (formerly Swagger) can be adapted for JSON-RPC documentation:

Approach 1: Single Endpoint with Method Parameter

{
  "openapi": "3.0.0",
  "info": {
    "title": "JSON-RPC API",
    "version": "1.0.0"
  },
  "paths": {
    "/api/jsonrpc": {
      "post": {
        "summary": "JSON-RPC endpoint",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  { "$ref": "#/components/schemas/UserGetRequest" },
                  { "$ref": "#/components/schemas/UserCreateRequest" }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC Response",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/UserGetResponse" },
                    { "$ref": "#/components/schemas/UserCreateResponse" }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "UserGetRequest": {
        "type": "object",
        "required": ["jsonrpc", "method", "id"],
        "properties": {
          "jsonrpc": { "type": "string", "enum": ["2.0"] },
          "method": { "type": "string", "enum": ["user.get"] },
          "params": {
            "type": "object",
            "properties": {
              "userId": { "type": "integer" }
            },
            "required": ["userId"]
          },
          "id": { "type": "string" }
        }
      },
      "UserGetResponse": {
        "type": "object",
        "required": ["jsonrpc", "result", "id"],
        "properties": {
          "jsonrpc": { "type": "string", "enum": ["2.0"] },
          "result": {
            "type": "object",
            "properties": {
              "id": { "type": "integer" },
              "name": { "type": "string" },
              "email": { "type": "string" }
            }
          },
          "id": { "type": "string" }
        }
      }
      // Additional schemas for other methods...
    }
  }
}

Approach 2: Virtual Endpoints for Methods

A more intuitive approach is to create "virtual" endpoints for each JSON-RPC method:

{
  "openapi": "3.0.0",
  "info": {
    "title": "JSON-RPC API",
    "version": "1.0.0"
  },
  "paths": {
    "/api/jsonrpc/user.get": {
      "post": {
        "summary": "Get user details",
        "description": "Retrieve information about a specific user",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": { 
                    "type": "integer",
                    "description": "ID of the user to retrieve"
                  }
                },
                "required": ["userId"]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "User details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": { "type": "integer" },
                    "name": { "type": "string" },
                    "email": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/jsonrpc/user.create": {
      "post": {
        "summary": "Create a user",
        // Additional documentation...
      }
    }
    // Additional virtual endpoints...
  }
}

Note: While these aren't actual separate endpoints, this approach makes the documentation more intuitive for API consumers.

Swagger UI for JSON-RPC

Once you've created an OpenAPI specification for your JSON-RPC API, you can use Swagger UI to provide an interactive documentation experience:

Implementation Steps:

  1. Create an OpenAPI specification for your JSON-RPC API using one of the approaches above
  2. Set up Swagger UI in your project (available as npm package, Docker container, or CDN)
  3. Configure Swagger UI to use your OpenAPI specification file
  4. Optionally customize the UI to better represent JSON-RPC concepts

Example Setup:

// In an Express.js application
const express = require('express');
const swaggerUi = require('swagger-ui-express');
const jsonRpcOpenApi = require('./jsonrpc-openapi.json');

const app = express();

// Set up Swagger UI
app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(jsonRpcOpenApi, {
  customCss: '.swagger-ui .opblock-summary-path { display: none }', // Optional customization
  customSiteTitle: "JSON-RPC API Documentation"
}));

app.listen(3000, () => {
  console.log('API documentation available at http://localhost:3000/api/docs');
});

Custom JSON-RPC Documentation

For the best documentation experience, you may want to create a custom documentation system specifically designed for JSON-RPC:

Key Components of Custom Documentation

Method Catalog

Organize methods by namespace with clear descriptions of purpose and behavior

Interactive Request Builder

Allow users to construct and test JSON-RPC requests with parameter validation

Schema Documentation

Visual representation of parameter and result schemas with field descriptions

Error Code Reference

Comprehensive list of error codes with explanations and resolution steps

Implementation Options:

  • Use a static site generator like Docusaurus or VuePress
  • Build a custom React/Vue application with interactive components
  • Generate documentation from code comments using a tool like JSDoc
  • Create a documentation generator that reads your JSON-RPC method definitions

Documentation Best Practices

Method Organization

Group related methods with consistent naming conventions. Use namespaces like "user.get", "user.create" to organize functionality.

Parameter Documentation

Document all parameters with types, constraints, and examples. For complex objects, use tables or expandable sections to show nested properties.

Error Handling

Document all possible error codes, including standard JSON-RPC errors and application-specific errors. Include troubleshooting guidance.

Versioning

Clearly indicate API version in the documentation. Document breaking changes and provide migration guides when updating the API.

Code Examples

Provide code examples in multiple programming languages. Show both the raw JSON-RPC request/response and client library usage.

Tip: Generate documentation directly from your JSON-RPC method implementations to ensure it stays in sync with your code. Consider using JSON Schema to validate both your implementation and documentation.