Swagger paths define the endpoints of your API, and operations specify the HTTP methods and details for interacting with those endpoints.

Let’s look at an actual Swagger definition and see these concepts in action. Imagine we have a simple API for managing a list of "widgets."

openapi: 3.0.0
info:
  title: Widget API
  version: 1.0.0
paths:
  /widgets:
    get:
      summary: List all widgets
      responses:
        '200':
          description: A list of widgets.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Widget'
    post:
      summary: Create a new widget
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewWidget'
      responses:
        '201':
          description: Widget created successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Widget'
  /widgets/{widgetId}:
    get:
      summary: Get a specific widget by ID
      parameters:
        - name: widgetId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the widget to retrieve.
      responses:
        '200':
          description: The requested widget.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Widget'
    put:
      summary: Update an existing widget
      parameters:
        - name: widgetId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the widget to update.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateWidget'
      responses:
        '200':
          description: Widget updated successfully.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Widget'
    delete:
      summary: Delete a widget
      parameters:
        - name: widgetId
          in: path
          required: true
          schema:
            type: string
          description: The ID of the widget to delete.
      responses:
        '204':
          description: Widget deleted successfully.

components:
  schemas:
    Widget:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
    NewWidget:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
    UpdateWidget:
      type: object
      properties:
        name:
          type: string
        description:
          type: string

In this example, the paths object is the core. It’s a map where each key is a URL path.

  • /widgets is a path.
  • /widgets/{widgetId} is another path. The {widgetId} part signifies a path parameter, a dynamic segment that will be replaced by an actual ID when a request is made.

Underneath each path, you define the operations that can be performed on that path. These are the standard HTTP methods: get, post, put, delete, patch, options, head, and trace.

For the /widgets path:

  • The get operation describes how to retrieve a list of all widgets. It specifies a 200 response with a JSON array of Widget objects.
  • The post operation details how to create a new widget. It requires a requestBody containing a NewWidget object and returns a 201 status code with the newly created Widget.

For the /widgets/{widgetId} path:

  • The get operation retrieves a single widget. Notice the parameters section. It explicitly defines widgetId as a path parameter, meaning it’s expected to be part of the URL itself (e.g., /widgets/abc-123).
  • The put operation allows updating an existing widget, again using the widgetId path parameter and expecting an UpdateWidget object in the request body.
  • The delete operation removes a widget, identified by its widgetId.

This structure provides a clear contract for how clients should interact with your API. The summary and description fields are crucial for human readability and documentation generation. The responses object outlines all possible outcomes of an operation, including status codes, descriptions, and the structure of the response body (content and schema).

The most surprising thing is how much of your API’s behavior is encoded declaratively in these paths and operations, without needing to write a single line of server-side code for the API definition itself. It’s a blueprint that tools can then use to generate client SDKs, server stubs, and interactive documentation.

When you define a path parameter like widgetId and specify its in: path and required: true, you’re telling the system that this value must be present in the URL. If a client tries to call /widgets/ without an ID, or if the server-side implementation doesn’t correctly extract and use that ID from the URL, the request will fail. This strictness is what makes APIs predictable.

The real power comes from how these definitions are used. A tool like Swagger UI can take this definition and render an interactive API explorer. A developer can then click "Try it out," fill in a widgetId like a1b2c3d4 for the /widgets/{widgetId} GET request, and see the live API response without writing any client code. This dramatically speeds up development and testing.

When you define schemas for your request and response bodies, like Widget, NewWidget, and UpdateWidget, you’re not just describing data; you’re enabling automatic validation. If a client sends a POST to /widgets with a JSON body that’s missing the required name field for NewWidget, the API framework (if configured correctly) can automatically reject the request with a 400 Bad Request error, citing the validation failure, before your application logic even runs.

The way path parameters are defined, including their schema and description, directly influences how servers must route and process incoming requests. A server framework will parse the incoming URL, match it against the defined paths, extract the values from the dynamic segments (like widgetId), and make them available to your handler function. If the type defined in the schema is integer but the client sends abc, the framework can often perform an automatic type coercion or throw an error.

Once you’ve mastered paths and operations, you’ll want to explore how to reuse definitions across your API using $ref for schemas and parameters, and how to handle different content types in request and response bodies.

Want structured learning?

Take the full Swagger course →