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
| Option | Short | Description |
|---|---|---|
--config | Path to merge configuration JSON file | |
--output | -o | Output file path (default: merged-openapi.json) |
--title | API title for merged specification | |
--version | API version for merged specification | |
--schema-conflict | Strategy for schema conflicts: rename, first-wins, or fail (default: rename) | |
--verbose | -v | Show 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:
| Property | Required | Description |
|---|---|---|
title | Yes | The title of the merged API |
version | Yes | The version of the merged API |
description | No | A 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:
| Property | Required | Description |
|---|---|---|
path | Yes | File path to the OpenAPI specification |
pathPrefix | No | Prefix to prepend to all paths (e.g., /users) |
operationIdPrefix | No | Prefix to prepend to all operationIds (e.g., users_) |
name | No | Friendly 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
| Component | Behavior |
|---|---|
| Paths | Combined from all sources; path prefixes applied if configured |
| Schemas | Deduplicated if identical; conflicts handled per strategy |
| Security Schemes | Combined; duplicates keep first definition |
| Tags | Combined with descriptions preserved |
| Servers | Only servers from config are used; source servers ignored |
| Operation IDs | Prefixes applied if configured; duplicates generate warnings |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success (may have warnings) |
| 1 | Configuration error |
| 2 | Merge error (schema conflict with fail strategy) |
| 3 | Input 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
Use path prefixes to namespace APIs from different services and avoid path conflicts. This also makes the merged API easier to navigate.
Apply operation ID prefixes to ensure unique operation IDs across services — important for SDK generation.
The rename strategy is the safest default, automatically handling conflicts while preserving all schemas.
Always check the output for warnings, especially when merging many specifications.