OpenAPI Overlays provide a powerful way to modify OpenAPI specifications without
directly editing the source files. This is especially useful when you need to:
Apply consistent changes across multiple API versions
Add Zuplo-specific extensions to third-party OpenAPI specs
Maintain separation between base API definitions and environment-specific
configurations
Automate modifications as part of your build or deployment process
What are OpenAPI Overlays
OpenAPI Overlays are JSON or YAML documents that describe modifications to be
applied to an OpenAPI specification. They use a simple, declarative syntax to
target specific parts of your API definition and apply changes.
The Zuplo CLI provides the
openapi overlay command to apply these
overlays to your OpenAPI files.
Basic Usage
The basic syntax for applying an overlay is:
npx zuplo openapi overlay \
--input openapi.json \
--overlay changes.json \
--output result.json
You can also use the --watch flag to automatically reapply overlays when files
change during development:
npx zuplo openapi overlay \
--input openapi.json \
--overlay changes.json \
--output result.json \
--watch
Example 1: Modifying Route Summaries and Descriptions
One common use case is updating the summary and description fields of your API
routes to improve documentation or add context-specific information. Overlays
can both modify existing properties and add missing properties.
{
"openapi" : "3.1.0" ,
"info" : {
"title" : "My API" ,
"version" : "1.0.0"
},
"paths" : {
"/users/{userId}" : {
"get" : {
"summary" : "Get user" ,
"description" : "Retrieves a user" ,
"operationId" : "getUser"
}
},
"/products" : {
"get" : {
"operationId" : "listProducts"
}
}
}
}
Notice that /users/{userId} already has a summary and description (which we'll
enhance), while /products has neither (which we'll add).
{
"overlay" : "1.0.0" ,
"info" : {
"title" : "Enhanced Documentation Overlay" ,
"version" : "1.0.0"
},
"actions" : [
{
"target" : "$.paths['/users/{userId}'].get" ,
"description" : "Enhance existing summary and description" ,
"update" : {
"summary" : "Get User by ID" ,
"description" : "Retrieves detailed information about a specific user by their unique identifier. This endpoint requires authentication and returns comprehensive user profile data including preferences and account settings."
}
},
{
"target" : "$.paths['/products'].get" ,
"description" : "Add missing summary and description" ,
"update" : {
"summary" : "List All Products" ,
"description" : "Returns a paginated list of all products in the catalog. Supports filtering by category, price range, and availability status."
}
}
]
}
npx zuplo openapi overlay \
--input openapi.json \
--overlay overlay.json \
--output openapi-enhanced.json
Example 2: Adding Parameter Schemas
Overlays are excellent for adding parameter definitions to your OpenAPI spec,
especially when working with APIs that have incomplete documentation.
Adding Path Parameters
{
"overlay" : "1.0.0" ,
"info" : {
"title" : "Add Path Parameters" ,
"version" : "1.0.0"
},
"actions" : [
{
"target" : "$.paths['/users/{userId}'].get" ,
"update" : {
"parameters" : [
{
"name" : "userId" ,
"in" : "path" ,
"required" : true ,
"description" : "The unique identifier of the user" ,
"schema" : {
"type" : "string" ,
"format" : "uuid" ,
"example" : "123e4567-e89b-12d3-a456-426614174000"
}
}
]
}
},
{
"target" : "$.paths['/orders/{orderId}/items/{itemId}'].get" ,
"update" : {
"parameters" : [
{
"name" : "orderId" ,
"in" : "path" ,
"required" : true ,
"description" : "The order identifier" ,
"schema" : {
"type" : "integer" ,
"minimum" : 1 ,
"example" : 12345
}
},
{
"name" : "itemId" ,
"in" : "path" ,
"required" : true ,
"description" : "The item identifier within the order" ,
"schema" : {
"type" : "integer" ,
"minimum" : 1 ,
"example" : 67890
}
}
]
}
}
]
}
Adding Query Parameters
{
"overlay" : "1.0.0" ,
"info" : {
"title" : "Add Query Parameters" ,
"version" : "1.0.0"
},
"actions" : [
{
"target" : "$.paths['/products'].get" ,
"update" : {
"parameters" : [
{
"name" : "category" ,
"in" : "query" ,
"required" : false ,
"description" : "Filter products by category" ,
"schema" : {
"type" : "string" ,
"enum" : [ "electronics" , "clothing" , "home" , "sports" ],
"example" : "electronics"
}
},
{
"name" : "minPrice" ,
"in" : "query" ,
"required" : false ,
"description" : "Minimum price filter (inclusive)" ,
"schema" : {
"type" : "number" ,
"format" : "float" ,
"minimum" : 0 ,
"example" : 9.99
}
},
{
"name" : "maxPrice" ,
"in" : "query" ,
"required" : false ,
"description" : "Maximum price filter (inclusive)" ,
"schema" : {
"type" : "number" ,
"format" : "float" ,
"minimum" : 0 ,
"example" : 99.99
}
},
{
"name" : "page" ,
"in" : "query" ,
"required" : false ,
"description" : "Page number for pagination" ,
"schema" : {
"type" : "integer" ,
"minimum" : 1 ,
"default" : 1 ,
"example" : 1
}
},
{
"name" : "limit" ,
"in" : "query" ,
"required" : false ,
"description" : "Number of items per page" ,
"schema" : {
"type" : "integer" ,
"minimum" : 1 ,
"maximum" : 100 ,
"default" : 20 ,
"example" : 20
}
}
]
}
}
]
}
npx zuplo openapi overlay \
--input openapi.json \
--overlay add-query-params.json \
--output openapi-with-params.json
Example 3: Adding Zuplo Route Extensions
One of the more common uses of overlays is to add the Zuplo-specific extensions
like x-zuplo-route to your OpenAPI files. This allows you to configure
policies, handlers, and CORS settings without modifying your base OpenAPI
specification.
{
"overlay" : "1.0.0" ,
"info" : {
"title" : "Zuplo Route Extensions" ,
"version" : "1.0.0"
},
"actions" : [
{
"target" : "$.paths['/users/{userId}'].get" ,
"update" : {
"x-zuplo-route" : {
"corsPolicy" : "anything-goes" ,
"handler" : {
"export" : "default" ,
"module" : "$import(./modules/my-handler)" ,
"options" : {
"someOption" : true
}
},
"policies" : {
"inbound" : [ "api-key-inbound" ]
}
}
}
},
{
"target" : "$.paths['/products'].get" ,
"update" : {
"x-zuplo-route" : {
"corsPolicy" : "anything-goes" ,
"handler" : {
"export" : "default" ,
"module" : "$import(@zuplo/runtime)" ,
"options" : {
"backend" : "backend-1"
}
},
"policies" : {
"inbound" : [
"api-key-inbound" ,
{
"name" : "rate-limit-inbound" ,
"policyType" : "rate-limit-inbound" ,
"handler" : {
"export" : "default" ,
"module" : "$import(@zuplo/runtime)" ,
"options" : {
"rateLimitBy" : "ip" ,
"requestsAllowed" : 100 ,
"timeWindowMinutes" : 1
}
}
}
],
"outbound" : [ "log-response-outbound" ]
}
}
}
},
{
"target" : "$.paths['/admin/settings'].put" ,
"update" : {
"x-zuplo-route" : {
"corsPolicy" : "none" ,
"handler" : {
"export" : "default" ,
"module" : "$import(./modules/admin-handler)" ,
"options" : {}
},
"policies" : {
"inbound" : [
"api-key-inbound" ,
{
"name" : "jwt-auth" ,
"policyType" : "jwt-auth-inbound" ,
"handler" : {
"export" : "JwtInboundPolicy" ,
"module" : "$import(@zuplo/runtime)" ,
"options" : {
"issuer" : "https://auth.example.com" ,
"audience" : "api.example.com" ,
"jwkUrl" : "https://auth.example.com/.well-known/jwks.json"
}
}
}
]
}
}
}
}
]
}
npx zuplo openapi overlay \
--input openapi.json \
--overlay zuplo-routes.json \
--output openapi-zuplo.json
This overlay adds:
CORS policies to control cross-origin requests
Request handlers that specify how Zuplo should process the request
Inbound policies like API key authentication, JWT validation, and rate
limiting
Outbound policies for logging and response transformation
Combining Multiple Overlays
You can apply multiple overlays sequentially to build up complex configurations:
# First add parameters
npx zuplo openapi overlay \
--input openapi.json \
--overlay add-params.json \
--output temp.json
# Then add Zuplo extensions
npx zuplo openapi overlay \
--input temp.json \
--overlay zuplo-routes.json \
--output final.json
# Clean up temporary file
rm temp.json
Or create a script that chains the commands:
Terminal apply-overlays.sh
#!/bin/bash
npx zuplo openapi overlay -i openapi.json -l docs.json -o step1.json && \
npx zuplo openapi overlay -i step1.json -l params.json -o step2.json && \
npx zuplo openapi overlay -i step2.json -l zuplo.json -o final.json && \
rm step1.json step2.json
Using Overlays in Your Build Process
Integrate overlay application into your CI/CD pipeline by adding it to your
build scripts:
{
"scripts" : {
"build:openapi" : "npx zuplo openapi overlay -i src/openapi.json -l overlays/production.json -o dist/openapi.json" ,
"build:openapi:dev" : "npx zuplo openapi overlay -i src/openapi.json -l overlays/development.json -o dist/openapi.json" ,
"watch:openapi" : "npx zuplo openapi overlay -i src/openapi.json -l overlays/development.json -o dist/openapi.json --watch"
}
}
JSONPath Target Syntax
Overlays use JSONPath syntax to target specific parts of your OpenAPI document.
Here are common patterns:
{
"actions" : [
{
"target" : "$.paths['/users/{userId}'].get" ,
"description" : "Target a specific operation"
},
{
"target" : "$.paths['/users/{userId}'].get.parameters[0]" ,
"description" : "Target the first parameter"
},
{
"target" : "$.paths['/users/*'].get" ,
"description" : "Target all GET operations under /users"
},
{
"target" : "$.components.schemas['User']" ,
"description" : "Target a schema definition"
}
]
}
Best Practices
Version Control Overlays : Store overlay files in version control
alongside your OpenAPI specs
Use Descriptive Names : Name overlay files clearly to indicate their
purpose (for example, add-auth-policies.json, staging-config.json)
Keep Overlays Focused : Create separate overlay files for different
concerns rather than one large overlay
Test Overlay Output : Always validate the resulting OpenAPI file after
applying overlays
Document Your Overlays : Add comments in the info.title and
info.description fields to explain what each overlay does
Use Watch Mode for Development : The --watch flag makes iterative
development faster
Next Steps
Last modified on November 18, 2025