Your Swagger/OpenAPI definition can be a single source of truth for all your environments, but it needs a little help to know where each one lives.

Let’s say you have a Swagger definition that looks something like this:

swagger: "2.0"
info:
  version: 1.0.0
  title: My Awesome API
paths:
  /users:
    get:
      summary: Get a list of users
      responses:
        "200":
          description: A list of users
          schema:
            type: array
            items:
              $ref: "#/definitions/User"
definitions:
  User:
    type: object
    properties:
      id:
        type: string
      name:
        type: string

This definition is great for describing your API, but it doesn’t tell tools where to find it. That’s where the host, basePath, and schemes fields come in.

The host is the domain name or IP address where your API is served. basePath is the prefix for all API paths (e.g., /v1). schemes specifies the protocol (e.g., http or https).

To manage different environments, you’ll typically define these fields for each environment.

Dev Environment

For local development, you might be running your API on localhost:3000.

swagger: "2.0"
info:
  version: 1.0.0
  title: My Awesome API
host: localhost:3000
basePath: /
schemes:
  - http
paths:
  /users:
    get:
      summary: Get a list of users
      responses:
        "200":
          description: A list of users
          schema:
            type: array
            items:
              $ref: "#/definitions/User"
definitions:
  User:
    type: object
    properties:
      id:
        type: string
      name:
        type: string

This tells tools that when they refer to My Awesome API, they should look at http://localhost:3000.

Staging Environment

Your staging environment might be hosted on a subdomain like staging.myapi.com.

swagger: "2.0"
info:
  version: 1.0.0
  title: My Awesome API
host: staging.myapi.com
basePath: /api/v1
schemes:
  - https
paths:
  /users:
    get:
      summary: Get a list of users
      responses:
        "200":
          description: A list of users
          schema:
            type: array
            items:
              $ref: "#/definitions/User"
definitions:
  User:
    type: object
    properties:
      id:
        type: string
      name:
        type: string

Now, tools know that My Awesome API on staging is reachable at https://staging.myapi.com/api/v1.

Production Environment

Your production environment could be api.myapi.com.

swagger: "2.0"
info:
  version: 1.0.0
  title: My Awesome API
host: api.myapi.com
basePath: /v1
schemes:
  - https
paths:
  /users:
    get:
      summary: Get a list of users
      responses:
        "200":
          description: A list of users
          schema:
            type: array
            items:
              $ref: "#/definitions/User"
definitions:
  User:
    type: object
    properties:
      id:
        type: string
      name:
        type: string

This configures tools to target https://api.myapi.com/v1 for production.

How to Manage Multiple Definitions

You have a few options for managing these environment-specific configurations:

  1. Separate Files: Maintain entirely separate swagger.yaml files for each environment (e.g., swagger-dev.yaml, swagger-staging.yaml, swagger-prod.yaml). This is simple but can lead to duplication.

  2. Templating: Use a templating engine (like Jinja2, Handlebars, or even simple shell scripts) to generate your final Swagger file from a base template and environment variables. This reduces duplication.

    For example, a Jinja2 template might look like:

    swagger: "2.0"
    info:
      version: 1.0.0
      title: My Awesome API
    
    host: {{ host }}
    
    
    basePath: {{ basePath }}
    
    schemes:
    
      - {{ scheme }}
    
    paths:
      # ... rest of your paths ...
    definitions:
      # ... your definitions ...
    

    And you’d render it like:

    export HOST="api.myapi.com"
    export BASE_PATH="/v1"
    export SCHEME="https"
    # Use your templating tool to render swagger.yaml.j2 into swagger.yaml
    
  3. External References (less common for host/basePath): While not ideal for host and basePath themselves, you can use $ref to pull in common parts of your definition from other files. This is more useful for reusable data models.

The "Single Source of Truth" Illusion

The idea is to have one definition that accurately describes your API, but the endpoints it refers to change. Tools like Swagger UI, Swagger Codegen, and API gateways use the host, basePath, and schemes to know where to send requests. By changing these fields, you’re effectively telling these tools to talk to a different instance of your API.

When you generate client SDKs using Swagger Codegen, the generated code will be configured to hit the host specified in your definition. If you generate for production, it uses the production host.

This approach is crucial for ensuring that your documentation, code generation, and API testing tools are always pointing to the correct environment.

The next hurdle is often managing API keys or authentication tokens for each environment.

Want structured learning?

Take the full Swagger course →