You design your API contract before you write a single line of code, and the Swagger definition is that contract.

Imagine you’re building a service that manages user profiles. Before you write any Go, Python, or Java code, you’d define what a user profile looks like and what operations you can perform on it.

openapi: 3.0.0
info:
  title: User Profile API
  version: 1.0.0
paths:
  /users/{userId}:
    get:
      summary: Get a user profile
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: User profile retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '404':
          description: User not found
    put:
      summary: Update a user profile
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'
      responses:
        '200':
          description: User profile updated successfully
        '404':
          description: User not found
components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
          format: int64
        username:
          type: string
        email:
          type: string
          format: email
      required:
        - id
        - username

This OpenAPI (formerly Swagger) definition is your single source of truth. It specifies the API’s endpoints (/users/{userId}), the HTTP methods allowed (GET, PUT), the expected parameters (a path parameter userId of type integer), the request body schema (a User object), and the possible responses (status codes and their content).

This upfront definition forces collaboration. The frontend team can start building their UI, mocking responses based on this spec, while the backend team can use tools to generate server stubs or client SDKs directly from this YAML. This eliminates the "it works on my machine" problem and ensures both sides are building against the same agreed-upon contract.

The "design-first" approach means you’re not just documenting code; you’re defining the interface of your system. This interface then drives the development of both the server and client implementations. Frameworks like Swagger Codegen or OpenAPI Generator can take this YAML file and produce boilerplate code for your server framework (e.g., Spring Boot, Express.js) or client SDKs in various languages.

For example, if you’re using swagger-codegen with a Java Spring Boot template, you’d run a command like this:

swagger-codegen generate -i swagger.yaml -l spring -o ./generated-server

This command takes your swagger.yaml file, uses the spring generator, and outputs a fully scaffolded Spring Boot project with controller interfaces and model classes already defined according to your spec. You then fill in the implementation details.

The most surprising thing about OpenAPI is how it transforms API development from an iterative, often error-prone process of writing code and then documenting it, into a declarative, specification-driven workflow. The spec is the primary artifact, and code generation becomes a natural consequence of that. This means if you change the YAML, you can regenerate code for both server and client, keeping everything in sync with minimal manual effort.

When you define a discriminator in your components/schemas section, you’re telling OpenAPI how to differentiate between different types of objects that share a common interface. For instance, if you have a Shape schema with subtypes like Circle and Square, the discriminator field (e.g., shapeType) in the request or response will indicate which specific subtype is being used. This is crucial for polymorphic data structures, allowing clients and servers to correctly serialize and deserialize complex object hierarchies without ambiguity.

The next step is understanding how to version your API using OpenAPI specifications.

Want structured learning?

Take the full Swagger course →