↓ Skip to main content

API OpenAPI API Design

API Design-First Workflow: Planning the OpenAPI Contract Each Sprint

The API design-first process we run each sprint: a backend and a frontend owner agree the OpenAPI contract, CI stands up a mock server, and both sides build in parallel. Example YAML included.

API Design-First Workflow: Planning the OpenAPI Contract Each Sprint

I wrote recently about API-first development with OpenAPI in general terms. This is the concrete version: the planning workflow we’re trialling on my team, and the example YAML that goes with it.

In short, the design-first process runs once per sprint:

  1. Pick two owners for the sprint’s API work, one from backend and one from frontend.
  2. Together they write or update the OpenAPI schema against the sprint’s requirements, and review it until both sides agree.
  3. The schema is committed to the repo. From here on it’s the contract.
  4. CI stands up a mock server from the committed schema.
  5. Frontend builds against the mock while backend implements the real endpoints.
  6. Once the real service is deployed, the spec it generates becomes the starting point for the next sprint’s schema.

Where this came from
#

The push came from our retro. The feedback was blunt about how little visibility anyone had into API planning: backend built endpoints against their own reading of the requirements, frontend discovered the response shapes after the fact, and the gaps surfaced at integration time — the most expensive possible moment. The proposal was to pull both sides into the planning phase and make the OpenAPI schema the thing everything hangs off.

The process
#

Each week we pick two representatives, one backend and one frontend. Their job is to understand the sprint’s requirements and get the planning OpenAPI schema to match. They review the schema together, revise whatever doesn’t line up, and by the end both sides know exactly what’s being built — before anyone builds it.

Once the revisions settle, the schema gets committed to the repo, and CI/CD generates a mock API from the yaml. When the sprint wraps up, a fresh yaml is cut covering the next sprint’s API requirements, and the cycle repeats.

Tooling notes
#

For visualising the yaml, Swagger Editor does the job: upload the file and see it rendered as a proper Swagger document.

The CI/CD pipeline needs to accept a yaml and stand up a mock server from it. Prism, from Stoplight, is a widely used option and handles OpenAPI 3.1:

npm install -g @stoplight/prism-cli
prism mock openapi.yaml

That serves every path in the schema on http://127.0.0.1:4010 and answers with the examples from the schema, so GET /lists/10 returns the example list from the appendix. It also enforces the contract: because the example declares an API key, a request without an X-API-Key header gets a 401, which is exactly the behaviour the frontend should be building against. Inside a container, add -h 0.0.0.0 so it’s reachable from outside. openapi-mock is a lighter alternative that fills responses with random data.

It’s also worth linting the schema before the mock goes up, so a broken contract fails the build instead of confusing the frontend. Spectral needs a one-line ruleset to get going:

npm install -g @stoplight/spectral-cli
echo 'extends: ["spectral:oas"]' > .spectral.yaml
spectral lint openapi.yaml

Finally, this schema is a planning artefact only. Once the real API is deployed, the service should generate its own openapi.json, which you can download and feed back into the next round of planning.

Why bother
#

Because the arguments move to the cheapest possible place. With a frontend and a backend engineer in the room while the schema is written, misunderstandings get caught before they become code. The API requirements are agreed once instead of renegotiated mid-sprint, the mock server means frontend starts immediately rather than waiting, and far fewer critical details slip through — they’d have to get past two pairs of eyes and a written contract.

None of this is revolutionary. It’s just planning made concrete enough that a machine can serve it back to you as a mock — which, it turns out, is about the level of concrete a plan needs to be.

Appendix A - yaml example
#

openapi: 3.1.0
info:
  title: MyApp Sprint 3
  description: Planning schema for sprint 3. Drives the mock server only.
  version: 0.3.0
servers:
  - url: https://api-mock.example.com/v1
tags:
  - name: lists
    description: Saved lists feature (PROJ-123)
security:
  - api_key: []
paths:
  /lists:
    get:
      tags: [lists]
      summary: Return the current user's saved lists
      operationId: getLists
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/List'
  /lists/{listId}:
    get:
      tags: [lists]
      summary: Find a list by ID
      operationId: getListById
      parameters:
        - name: listId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/List'
        '404':
          description: List not found
components:
  schemas:
    List:
      type: object
      required: [id, title]
      properties:
        id:
          type: integer
          format: int64
          examples: [10]
        title:
          type: string
          examples: [Weekend reading]
        itemCount:
          type: integer
          examples: [7]
  securitySchemes:
    api_key:
      type: apiKey
      name: X-API-Key
      in: header