Skip to main content

OpenAPI Merge Tool

Merge multiple OpenAPI specification files into a single unified specification. Ideal for microservice architectures where each service generates its own OpenAPI spec, but you need a combined specification for documentation portals, client SDK generation, or API gateway configuration.

Installation

Global Tool

Install globally to use from any directory:

dotnet tool install -g Oproto.Lambda.OpenApi.Merge.Tool

Run using:

dotnet openapi-merge merge [options] [files...]

Local Tool

Install as a local tool for project-specific usage:

# Create a tool manifest if you don't have one
dotnet new tool-manifest

# Install the tool locally
dotnet tool install Oproto.Lambda.OpenApi.Merge.Tool

Run using:

dotnet tool run openapi-merge merge [options] [files...]

CLI Usage

The merge tool supports two modes: direct invocation with command-line arguments, or configuration file-based merging.

Direct Invocation

Merge multiple OpenAPI files directly:

dotnet openapi-merge merge --title "My API" --version "1.0.0" -o merged.json api1.json api2.json api3.json

Configuration File

Use a JSON configuration file for complex merge scenarios:

dotnet openapi-merge merge --config merge.config.json

Command Options

OptionShortDescription
--configPath to merge configuration JSON file
--output-oOutput file path (default: merged-openapi.json)
--titleAPI title for merged specification
--versionAPI version for merged specification
--schema-conflictStrategy for schema conflicts: rename, first-wins, or fail (default: rename)
--verbose-vShow detailed progress and warnings

Examples

# Basic merge with required metadata
dotnet openapi-merge merge --title "Platform API" --version "2.0.0" -o platform-api.json \
users-api.json products-api.json orders-api.json

# Merge with verbose output
dotnet openapi-merge merge -v --title "My API" --version "1.0.0" api1.json api2.json

# Use first-wins strategy for schema conflicts
dotnet openapi-merge merge --schema-conflict first-wins --title "API" --version "1.0" api1.json api2.json

Configuration File Format

The configuration file provides full control over the merge process, including path prefixes and operation ID prefixes for each source.

Example Configuration

{
"info": {
"title": "Acme Platform API",
"version": "1.0.0",
"description": "Unified API documentation for all Acme services"
},
"servers": [
{ "url": "https://api.acme.com", "description": "Production" },
{ "url": "https://staging-api.acme.com", "description": "Staging" }
],
"sources": [
{
"path": "./services/users/openapi.json",
"pathPrefix": "/users",
"operationIdPrefix": "users_",
"name": "Users Service"
},
{
"path": "./services/products/openapi.json",
"pathPrefix": "/products",
"operationIdPrefix": "products_",
"name": "Products Service"
},
{
"path": "./services/orders/openapi.json",
"pathPrefix": "/orders",
"name": "Orders Service"
}
],
"output": "./merged-openapi.json",
"schemaConflict": "rename"
}

Configuration Properties

info (required)

Metadata for the merged specification:

PropertyRequiredDescription
titleYesThe title of the merged API
versionYesThe version of the merged API
descriptionNoA description of the merged API

servers (optional)

Array of server definitions. Source servers are ignored; only configured servers appear in the output.

sources (required)

Array of source specifications to merge:

PropertyRequiredDescription
pathYesFile path to the OpenAPI specification
pathPrefixNoPrefix to prepend to all paths (e.g., /users)
operationIdPrefixNoPrefix to prepend to all operationIds (e.g., users_)
nameNoFriendly name for warnings/errors (defaults to filename)

output (required)

File path for the merged specification output.

schemaConflict (optional)

Strategy for handling schema naming conflicts. Default is rename.

Schema Conflict Strategies

When multiple sources define schemas with the same name, the merge tool uses one of three strategies:

rename (default)

Conflicting schemas are renamed using the source name as a prefix. All $ref references are automatically updated.

Example: If both users-api.json and products-api.json define a Response schema with different structures:

  • First schema remains as Response
  • Second schema becomes Products_Response
  • All references in the products API are updated to #/components/schemas/Products_Response

first-wins

The first schema encountered is kept; subsequent schemas with the same name are ignored. A warning is generated for each ignored schema.

Use case: When schemas with the same name are intentionally identical across services.

fail

The merge operation fails with an error when schema conflicts are detected.

Use case: When you want strict control and prefer to manually resolve conflicts.

Merge Behavior

What Gets Merged

ComponentBehavior
PathsCombined from all sources; path prefixes applied if configured
SchemasDeduplicated if identical; conflicts handled per strategy
Security SchemesCombined; duplicates keep first definition
TagsCombined with descriptions preserved
ServersOnly servers from config are used; source servers ignored
Operation IDsPrefixes applied if configured; duplicates generate warnings

Exit Codes

CodeMeaning
0Success (may have warnings)
1Configuration error
2Merge error (schema conflict with fail strategy)
3Input validation error

Programmatic Usage

For programmatic usage, install the core library:

dotnet add package Oproto.Lambda.OpenApi.Merge

Then use it in your .NET applications:

using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
using Oproto.Lambda.OpenApi.Merge;

// Create configuration
var config = new MergeConfiguration
{
Info = new MergeInfoConfiguration
{
Title = "My API",
Version = "1.0.0",
Description = "Merged API specification"
},
Servers = new List<MergeServerConfiguration>
{
new() { Url = "https://api.example.com", Description = "Production" }
},
SchemaConflict = SchemaConflictStrategy.Rename
};

// Load source documents
var sources = new List<(SourceConfiguration, OpenApiDocument)>();
var reader = new OpenApiStreamReader();

foreach (var sourcePath in new[] { "api1.json", "api2.json" })
{
using var stream = File.OpenRead(sourcePath);
var doc = reader.Read(stream, out var diagnostic);

sources.Add((
new SourceConfiguration { Path = sourcePath, Name = Path.GetFileNameWithoutExtension(sourcePath) },
doc
));
}

// Merge
var merger = new OpenApiMerger();
var result = merger.Merge(config, sources);

// Check for warnings
foreach (var warning in result.Warnings)
{
Console.WriteLine($"Warning: {warning.Message}");
}

// Use the merged document
OpenApiDocument mergedDoc = result.Document;

Best Practices

Namespace Your APIs

Use path prefixes to namespace APIs from different services and avoid path conflicts. This also makes the merged API easier to navigate.

Use Operation ID Prefixes

Apply operation ID prefixes to ensure unique operation IDs across services — important for SDK generation.

Start with Rename Strategy

The rename strategy is the safest default, automatically handling conflicts while preserving all schemas.

Review Warnings

Always check the output for warnings, especially when merging many specifications.