iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🦁

Setting up a Mock Server with OpenAPI and Prism

に公開

Introduction

In this article, I will introduce how to set up a mock server from an OpenAPI definition using Stoplight Prism.

Using a Todo API as a sample, I will explain everything step-by-step, from basic usage to running it with Docker.

The sample project introduced in this article is available in the following repository:

https://github.com/tonbiattack/prism-mock-server-sample

What is Prism?

Prism is an OSS tool that reads OpenAPI (v2/v3) definition files and allows you to launch an HTTP mock server without an actual backend implementation.

# This alone starts the mock server
prism mock openapi.yaml

As long as you have an OpenAPI file, you can run and verify the API without waiting for backend implementation.

Why use Prism?

"Agreeing on specifications with OpenAPI and then developing in parallel" seems smooth at first glance. However, in practice, you often encounter situations like these:

  • The frontend started development and realized the nested response structure is difficult to use. However, the backend was already implemented, making changes costly to coordinate.
  • Development progressed while putting off the definition of error cases (401, 404, 500), eventually forcing a bulk implementation of frontend error handling just before integration testing.
  • I thought "writing the OpenAPI meant we agreed," but upon actually calling the mock, several misalignments were discovered.
  • Because I was developing by fetching local JSON files, I got stuck when integrating with the backend. Since JSON only ever returns successful responses, I couldn't verify header handling, authentication errors, or network error behavior at all.

What these have in common is "not verifying working things early enough."

Using Prism, you can launch a mock server immediately after writing the OpenAPI and call it from the frontend.

Write OpenAPI → Instant launch with Prism → Verify by calling from frontend → Refine design

Since you can discover design problems before implementation, you can significantly reduce destructive changes later on.

When to use / When not to use Prism

Prism is not a silver bullet. It is effective to be aware of when to use it.

It is suitable for the following situations:

  • Frontend-first development (you want to build the UI without waiting for backend implementation)
  • API design review stage (you want to write OpenAPI and verify behavior immediately)
  • Verifying error handling (you can return any status code with the Prefer header)
  • Demos to teams or stakeholders (you can show how the API works without implementation)
  • When designers or members with less development experience join the project (they can experience "what returns when this button is pressed" without code, as they can actually hit the API and check the behavior)
  • When a frontend junior engineer is learning API integration (they can proceed with implementation while checking response structures and error cases at hand, even without a real backend)
  • When a backend junior engineer wants to solidify specifications before starting implementation (they can write the OpenAPI first, verify behavior with a mock, and focus on internal implementation after deciding the interface)

It is not suitable for the following situations:

  • Verifying behavior that includes DB access or complex business logic (mocks can only return fixed values)
  • Performance testing (the speed of the implementation and the speed of the mock are different things)
  • Verifying the actual behavior of authentication/authorization (Prism stops at schema validation)

Comparison with Other Mocking Methods

There are several ways to set up a mock server. I will summarize the reasons for choosing Prism.

Method Features Suitable for
Prism Automatic generation from OpenAPI, no additional code (weak dynamic logic) OpenAPI-first development
Hand-written stubs (Express, etc.) Flexible but increases code volume Mocks requiring complex logic
WireMock HTTP mock tool from Java, strong for enterprise use (also runs in Docker) Contract testing or API testing
MSW (Mock Service Worker) Mocking that completes on the frontend side Browser mocking during React / Next.js development

If you are managing specifications with OpenAPI, Prism is the choice with the lowest overhead. However, in situations requiring state management or complex conditional branching, hand-written stubs are more suitable.

Installation

npm install --save-dev @stoplight/prism-cli

If you want to use it globally:

npm install -g @stoplight/prism-cli

You can simplify mode switching by defining scripts in package.json.

{
  "scripts": {
    "mock": "prism mock openapi.yaml",
    "mock:verbose": "prism mock openapi.yaml --verboseLevel debug",
    "mock:dynamic": "prism mock openapi.yaml --dynamic"
  },
  "devDependencies": {
    "@stoplight/prism-cli": "^5.12.0"
  }
}

Start it with the following commands:

# Normal startup (returns example values as-is)
npm run mock

# Detailed log display
npm run mock:verbose

# Dynamic mode (generates random values from schema)
npm run mock:dynamic

Preparing the OpenAPI File

Prism returns the values defined in the OpenAPI example field for requests as-is. The more thoroughly you write the example, the more accurately your intended mock response will be returned.

# openapi.yaml
paths:
  /todos:
    get:
      summary: Get Todo List
      responses:
        '200':
          content:
            application/json:
              # The value written here is returned as-is
              example:
                - id: 1
                  title: "Go shopping"
                  status: "open"
                  createdAt: "2026-03-20T09:00:00Z"
                - id: 2
                  title: "Write report"
                  status: "done"
                  createdAt: "2026-03-21T10:00:00Z"

If you define error responses in the same way, you can call them using the Prefer header.

      responses:
        '401':
          description: Authentication Error
          content:
            application/json:
              example:
                error: "Unauthorized"
                message: "Authentication is required"
        '404':
          description: Resource Not Found
          content:
            application/json:
              example:
                error: "Not Found"
                message: "The specified Todo was not found"

Launching

Basic Startup (Static Mode)

npx prism mock openapi.yaml

When launched, a mock server will start at http://localhost:4010. It will return the value defined in example with the same content every time.

Dynamic Mode

npx prism mock openapi.yaml --dynamic

It ignores the example and generates random values from the schema type information. Since different values are returned each time, it is useful for verifying UI robustness.

Detailed Log Display

npx prism mock openapi.yaml --verboseLevel debug

It outputs all logs related to the request/response negotiation process.

Returning Error Types with the Prefer Header

By adding the Prefer: code=XXX header, you can force the return of any status code defined in openapi.yaml. Even if the backend hasn't implemented errors yet, you can build frontend error handling in advance.

# Return 401 Authentication Error
curl http://localhost:4010/todos/1 \
  -H "Prefer: code=401"

# Return 403 Permission Error
curl http://localhost:4010/todos/1 \
  -H "Prefer: code=403"

# Return 404
curl http://localhost:4010/todos/1 \
  -H "Prefer: code=404"

# Return 500 Server Error
curl http://localhost:4010/todos/1 \
  -H "Prefer: code=500"

The status code must be defined in the target endpoint of openapi.yaml. If you specify a code not in the definition, it will be ignored.

Running with Docker

With Docker, anyone can start it in the same environment without needing Node.js installed. It is suitable for sharing within teams or the company.

Dockerfile

FROM node:20-alpine

WORKDIR /app

# Installing dependencies
COPY package*.json ./
RUN npm install

# Copying the OpenAPI file
COPY openapi.yaml .

EXPOSE 4010

# Must bind to 0.0.0.0 to access from outside the container
CMD ["npx", "prism", "mock", "--host", "0.0.0.0", "openapi.yaml"]

docker-compose.yml

services:
  mock-server:
    build: .
    ports:
      - "4010:4010"  # Left is host, right is container port
    volumes:
      - ./openapi.yaml:/app/openapi.yaml:ro  # Mount openapi.yaml as read-only

Starting and Stopping

# Start
docker compose up --build

# Start in background
docker compose up -d --build

# Stop
docker compose down

Changing Ports

Changing Only the Host Port

Change only the left side (host side) of docker-compose.yml. The inside of the container remains 4010, so no changes to the Dockerfile are needed.

# docker-compose.yml
ports:
  - "8080:4010"  # Change host side to 8080

After changing, restart using the following commands:

# down: stop and remove running containers
# up --build: rebuild image and start containers
docker compose down && docker compose up --build

The access URL will be http://localhost:8080.

Changing Both Host and Container Ports

Change both docker-compose.yml and Dockerfile.

# docker-compose.yml
ports:
  - "8080:8080"
# Dockerfile
CMD ["npx", "prism", "mock", "--host", "0.0.0.0", "--port", "8080", "openapi.yaml"]

Restart in the same way after making changes.

docker compose down && docker compose up --build

Changing the Base URL

servers.url in openapi.yaml is for documentation notation and does not affect the actual access URL.

# openapi.yaml
servers:
  - url: http://localhost:8080  # Change to match the port (reference only)

If you want to change the path prefix (e.g., /api/v1/todos), change the paths definition itself.

# openapi.yaml
paths:
  /api/v1/todos:   # Change from /todos → /api/v1/todos

Since openapi.yaml is volume-mounted, changes are reflected without needing a container rebuild by simply editing the file.

Points to Note

--host 0.0.0.0 is Required

When starting Prism inside a Docker container, you must specify --host 0.0.0.0 to make it accessible from outside the container. The default 127.0.0.1 (localhost) is bound only inside the container, so requests from the host machine will not reach it.

# NG: Inaccessible from outside the container
CMD ["npx", "prism", "mock", "openapi.yaml"]

# OK: Bind to 0.0.0.0
CMD ["npx", "prism", "mock", "--host", "0.0.0.0", "openapi.yaml"]

Prefer Header Works Only with Defined Codes

Prefer: code=XXX can only return status codes defined in the target endpoint of openapi.yaml. Specifying a code not in the definition will be ignored. If you want to test error states, you must define them in openapi.yaml in advance.

Lack of Examples Can Lead to Unexpected Responses

Prism searches for response values in the following order:

  1. The endpoint's example field
  2. The first entry in the examples field
  3. The example field of each property in the schema
  4. If none of these exist, it auto-generates from the schema's type information

It works without writing an example, but the returned value will be a random value auto-generated from the schema. If you want to express the intent that "this endpoint returns this data," defining an example is more reliable.

Request Validation is Enabled by Default

Sending a request that doesn't match the schema will return 400 / 422. This is a specification of Prism and helps discover inconsistencies or omissions in the OpenAPI definition. On the other hand, if you want to intentionally send invalid requests to verify behavior, please check the schema in openapi.yaml.

Summary

Prism is a tool that allows you to immediately set up a mock server using only an OpenAPI file.

It is most effective in the phase of "running a mock immediately after deciding on the design." You can catch cumbersome response structures or missing error cases by calling it from the frontend. The correction cost is significantly lower than noticing these things after implementation has progressed.

By combining it with Docker, it can be shared across the team without environmental differences, and by utilizing example descriptions and the Prefer header, you can easily create a mock environment that covers both success and error paths.

Discussion