Schema Merging

Compose multiple service schemas into unified API documentation

The merger package composes schemas from multiple services into unified API documentation. This is essential for API gateways that present a single, federated API surface.

OpenAPI Merging

import "github.com/xraph/farp/merger"

m := merger.NewMerger(merger.DefaultMergerConfig())

result, err := m.Merge([]merger.ServiceSchema{
    {Manifest: userManifest, Schema: userOpenAPI},
    {Manifest: orderManifest, Schema: orderOpenAPI},
})

// result.Spec — merged OpenAPI 3.1.0 spec
// result.Conflicts — naming conflicts encountered
// result.Warnings — non-fatal issues

Configuration

config := merger.MergerConfig{
    DefaultConflictStrategy: farp.ConflictStrategyPrefix,
    MergedTitle:             "Federated API",
    MergedDescription:       "Unified API from all microservices",
    MergedVersion:           "1.0.0",
    IncludeServiceTags:      true,
    SortOutput:              true,
    Servers: []merger.Server{
        {URL: "https://api.example.com", Description: "Production"},
    },
}

Conflict Resolution

When multiple services define the same paths, components, or operation IDs, the merger applies a conflict strategy:

StrategyBehavior
prefix (default)Add service name prefix to conflicting items
errorReturn an error on any conflict
skipKeep the first occurrence, skip duplicates
overwriteKeep the last occurrence
mergeAttempt to merge operations within the same path

Per-Service Configuration

descriptor.Metadata = &farp.ProtocolMetadata{
    OpenAPI: &farp.OpenAPIMetadata{
        Composition: &farp.CompositionConfig{
            IncludeInMerged:   true,
            ConflictStrategy:  farp.ConflictStrategyPrefix,
            ComponentPrefix:   "UserSvc",
            TagPrefix:         "users",
            OperationIDPrefix: "user",
        },
    },
}

Conflict Types

TypeDescription
pathSame path in multiple services
componentSame component name
tagSame tag name
operationIdSame operation ID
securitySchemeSame security scheme name

Route-Aware Merging

The merger respects each service's RoutingConfig. Paths are adjusted based on mount strategy before merging:

user-service (strategy: "service")
  /users       →  /user-service/users

order-service (strategy: "versioned")
  /orders      →  /order-service/v2/orders

Component $ref references are updated automatically when prefixes are applied.

Multi-Protocol Merging

For services exposing multiple protocols, MultiProtocolMerger handles all schema types:

multiMerger := merger.NewMultiProtocolMerger(merger.DefaultMergerConfig())

result, err := multiMerger.MergeAll(manifests, func(hash string) (any, error) {
    return schemaCache.Get(hash)
})

// result.OpenAPI  — merged OpenAPI
// result.AsyncAPI — merged AsyncAPI
// result.GRPC     — merged gRPC
// result.ORPC     — merged oRPC

Gateway-Level Merging

The gateway client provides convenience methods:

// Merge all protocols for all services
result, err := gatewayClient.GenerateMergedSchemas(ctx, "")

// Merge for a specific service
result, err := gatewayClient.GenerateMergedSchemas(ctx, "user-service")

// Get merged OpenAPI as JSON
jsonData, err := gatewayClient.GetMergedOpenAPIJSON(ctx, "")

How is this guide?

On this page