DEV Community

Preecha
Preecha

Posted on

Why Is the Swagger Petstore Example a Bad REST API Design?

TL;DR

The Swagger Petstore is useful for demonstrating OpenAPI, but it is a poor REST design reference. It mixes singular and plural resource names, puts action verbs in URLs, uses incorrect HTTP status codes, exposes passwords in GET requests, returns bare arrays, and uses non-standard errors. Modern PetstoreAPI addresses these issues with consistent resource-oriented URLs, RFC 9457 error responses, pagination metadata, and production-ready API patterns.

Try Apidog today

Introduction

For more than a decade, Swagger Petstore has been the default example for learning OpenAPI. Millions of developers have studied it, copied its patterns, and used it as a starting point for production APIs.

The problem is that an OpenAPI example is not automatically a REST design reference.

Swagger Petstore demonstrates several anti-patterns:

  • Inconsistent resource naming
  • Action verbs in URLs
  • Incorrect HTTP status codes
  • Passwords in query parameters
  • Bare collection responses
  • Non-standard error formats

These patterns are easy to copy into production code because they appear in official documentation, tutorials, generated SDKs, and Swagger UI examples.

In this guide, you’ll learn how to identify these issues, why they matter, and how Modern PetstoreAPI addresses them. You’ll also see practical validation tests and a migration strategy for APIs based on the old Petstore design.

The Swagger Petstore Legacy Problem

Swagger Petstore was created in 2011 as a simple example for the Swagger specification, which is now OpenAPI. Its primary purpose was to demonstrate how to describe an API—not to define every REST API design best practice.

Why It Became the De Facto Standard

Developers commonly start learning OpenAPI with the official example. Swagger Petstore appears in:

  • OpenAPI documentation
  • API design tutorials
  • Swagger UI examples
  • Swagger Codegen examples
  • API design courses

This creates a common assumption:

If it is the official example, it must represent best practice.

As a result, developers copy its endpoint structure without evaluating whether the design is appropriate for production.

The Cost of Bad Examples

Anti-patterns compound over time:

  • Junior developers learn incorrect patterns without knowing they are problematic.
  • Code generators perpetuate the design, producing SDKs around flawed endpoints.
  • Documentation tools display the patterns as familiar examples.
  • Companies build internal APIs the same way, often reasoning that “it was good enough for Swagger.”

That is why flaws in a widely used example matter: they influence real API designs.

Critical REST Violations in Swagger Petstore

The following sections compare common Swagger Petstore patterns with a more consistent resource-oriented design.

1. Inconsistent Resource Naming

The violation

GET /pet/{petId}
GET /store/inventory
POST /pet
GET /user/{username}
Enter fullscreen mode Exit fullscreen mode

The API mixes singular and plural resource names.

Why it matters

REST resources commonly represent collections using plural nouns:

  • /pets represents the pets collection.
  • /pets/123 represents one pet in that collection.
  • /users represents the users collection.

Mixing /pet, /store, and /user makes the resource hierarchy harder to understand and maintain.

The Modern PetstoreAPI approach

GET /pets/{petId}
GET /stores/inventory
POST /pets
GET /users/{username}
Enter fullscreen mode Exit fullscreen mode

Use a consistent naming convention across the API:

  • Collections use plural nouns.
  • Individual resources use the collection path plus an identifier.
  • The same convention applies to every resource type.

Modern PetstoreAPI uses plural resource names consistently across its endpoints. Check the REST API documentation for the complete endpoint structure.

2. Action Verbs in URLs

The violation

GET /pet/findByStatus?status=available
GET /pet/findByTags?tags=tag1,tag2
GET /user/login?username=john&password=secret
GET /user/logout
Enter fullscreen mode Exit fullscreen mode

Why it matters

REST URLs should identify resources. The HTTP method already describes the operation:

  • GET retrieves a resource.
  • POST creates a resource or triggers a non-idempotent operation.
  • DELETE removes a resource.

A path such as /findByStatus exposes an action instead of modeling a resource query. Filtering belongs in query parameters.

The Modern PetstoreAPI approach

GET /pets?status=AVAILABLE
GET /pets?tags=tag1,tag2
POST /auth/login
POST /auth/logout
Enter fullscreen mode Exit fullscreen mode

Use query parameters for collection filters:

GET /pets?status=AVAILABLE&tags=cat&page=1&limit=20
Enter fullscreen mode Exit fullscreen mode

Use a separate authentication resource for login and logout rather than putting credentials in a user lookup URL.

Modern PetstoreAPI uses query parameters for filtering and separate authentication resources. See the authentication guide for proper authentication patterns.

3. Incorrect HTTP Status Codes

The violation

POST /pet
HTTP/1.1 200 OK
Enter fullscreen mode Exit fullscreen mode

A successful creation should normally use 201 Created.

DELETE /pet/{petId}
HTTP/1.1 200 OK

{
  "message": "Pet deleted"
}
Enter fullscreen mode Exit fullscreen mode

A successful deletion that does not return a response body should use 204 No Content.

Why it matters

HTTP status codes communicate the result of an operation:

  • 200 OK — the request succeeded and a response body is returned.
  • 201 Created — a resource was created.
  • 204 No Content — the request succeeded and there is no response body.
  • 400 Bad Request — the request is invalid.
  • 404 Not Found — the requested resource does not exist.

Using 200 for every successful operation makes clients infer behavior from the response body instead of using HTTP semantics.

The Modern PetstoreAPI approach

POST /pets
Content-Type: application/json

{
  "name": "Fluffy",
  "status": "AVAILABLE"
}
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 201 Created
Location: /pets/019b4132-70aa-764f-b315-e2803d882a24
Content-Type: application/json

{
  "id": "019b4132-70aa-764f-b315-e2803d882a24",
  "name": "Fluffy",
  "status": "AVAILABLE"
}
Enter fullscreen mode Exit fullscreen mode

For deletion:

DELETE /pets/019b4132-70aa-764f-b315-e2803d882a24
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 204 No Content
Enter fullscreen mode Exit fullscreen mode

Modern PetstoreAPI uses status codes according to their HTTP semantics and includes a Location header for created resources. Check the HTTP status codes guide for the complete mapping.

4. Bare Arrays Without Metadata

The violation

GET /pet/findByStatus?status=available
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "id": 1,
    "name": "Fluffy"
  },
  {
    "id": 2,
    "name": "Buddy"
  }
]
Enter fullscreen mode Exit fullscreen mode

Why it matters

A bare array leaves no standard place for collection metadata:

  • Pagination details
  • Total item counts
  • Navigation links
  • Additional collection-level information

Adding metadata later can also change the response shape and break clients.

The Modern PetstoreAPI approach

GET /pets?status=AVAILABLE&page=1&limit=20
Enter fullscreen mode Exit fullscreen mode
{
  "data": [
    {
      "id": "019b4132-70aa-764f-b315-e2803d882a24",
      "name": "Fluffy"
    },
    {
      "id": "019b4127-54d5-76d9-b626-0d4c7bfce5b6",
      "name": "Buddy"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalItems": 45,
    "totalPages": 3
  },
  "links": {
    "self": "/pets?status=AVAILABLE&page=1",
    "next": "/pets?status=AVAILABLE&page=2",
    "last": "/pets?status=AVAILABLE&page=3"
  }
}
Enter fullscreen mode Exit fullscreen mode

Wrapping collections gives clients a stable response structure and provides room for pagination and navigation metadata.

Modern PetstoreAPI wraps collections with data, pagination, and HATEOAS links. See the pagination guide for implementation details.

5. Missing Error Standards

The violation

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "code": 400,
  "message": "Invalid input"
}
Enter fullscreen mode Exit fullscreen mode

Why it matters

This format does not provide enough information for clients to handle errors consistently:

  • No error type identifier
  • No field-level validation details
  • No machine-readable error code
  • No standard media type
  • No request instance or context

The Modern PetstoreAPI approach

Use RFC 9457 Problem Details with the application/problem+json media type:

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Enter fullscreen mode Exit fullscreen mode
{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "instance": "/pets",
  "errors": [
    {
      "field": "name",
      "message": "Name is required",
      "code": "REQUIRED_FIELD"
    },
    {
      "field": "status",
      "message": "Status must be one of: AVAILABLE, PENDING, SOLD",
      "code": "INVALID_ENUM"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The standard top-level fields provide general error information, while the errors array gives clients field-level details.

Modern PetstoreAPI uses RFC 9457 Problem Details for errors. See the error handling guide for the complete format.

Security Issues in the Old Design

The endpoint design also creates avoidable security risks.

Passwords in GET Requests

The violation

GET /user/login?username=john&password=secret123
Enter fullscreen mode Exit fullscreen mode

Why it is dangerous

Query parameters are commonly recorded in:

  • Browser history
  • Web server logs
  • Proxy logs
  • Browser bookmarks
  • Referrer headers
  • Monitoring and analytics systems

A password in a URL can therefore be exposed to systems and people that should never receive it.

The Modern PetstoreAPI approach

Send credentials in the body of a POST request:

POST /auth/login
Content-Type: application/json

{
  "username": "john",
  "password": "secret123"
}
Enter fullscreen mode Exit fullscreen mode
HTTP/1.1 200 OK
Content-Type: application/json
Enter fullscreen mode Exit fullscreen mode
{
  "accessToken": "eyJhbGc...",
  "refreshToken": "eyJhbGc...",
  "expiresIn": 3600
}
Enter fullscreen mode Exit fullscreen mode

This keeps passwords out of the URL. Modern PetstoreAPI uses POST with JSON request bodies for authentication. See the authentication guide for OAuth 2.0 and JWT patterns.

API Keys in Query Parameters

The violation

GET /pet/123?api_key=abc123secret
Enter fullscreen mode Exit fullscreen mode

API keys in URLs can be logged, cached, bookmarked, or exposed through referrer headers.

The Modern PetstoreAPI approach

Use the Authorization header:

GET /pets/019b4132-70aa-764f-b315-e2803d882a24
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Enter fullscreen mode Exit fullscreen mode

Modern PetstoreAPI uses standard authorization headers for API keys and tokens. See the security guide for authentication patterns.

How Modern PetstoreAPI Addresses These Issues

Modern PetstoreAPI was built to demonstrate a more consistent REST API design.

REST Design

  • Plural resource names such as /pets, /orders, and /users
  • Resource-oriented URLs without action verbs
  • Correct HTTP status codes
  • Location headers for created resources
  • Collection wrappers with pagination metadata
  • RFC 9457 Problem Details for errors

Modern Standards

Modern PetstoreAPI includes examples using:

  • OpenAPI 3.2
  • RFC 9457 Problem Details
  • IETF rate-limiting headers
  • ISO 8601 date and time formats
  • UUIDv7 identifiers

Multi-Protocol Support

Unlike Swagger Petstore, which focuses on REST, Modern PetstoreAPI supports:

  • REST with OpenAPI 3.2
  • GraphQL
  • gRPC
  • WebSocket
  • Server-Sent Events (SSE)
  • MQTT
  • Webhooks
  • Model Context Protocol (MCP)

See the protocols guide for implementation details.

Business Logic Examples

Modern PetstoreAPI includes examples of:

  • Payment processing
  • Inventory management
  • Order fulfillment
  • Webhook notifications
  • AI-powered pet recommendations
  • Image upload and processing

Check the API documentation for the complete feature set.

Testing REST API Design with Apidog

Use an API client and automated tests to verify that your API follows the conventions in its specification.

Import and Validate an OpenAPI Specification

To import the Modern PetstoreAPI specification:

  1. Open Apidog.
  2. Select ImportOpenAPI.
  3. Enter https://petstoreapi.com/openapi.json.
  4. Review the imported endpoints and generated test cases.

Apidog can help identify issues such as:

  • Inconsistent resource naming
  • Missing or incorrect HTTP status codes
  • Invalid response structures
  • Authentication patterns that expose credentials

Test Resource Naming

Create a test that rejects singular resource paths when your API convention requires plural names:

pm.test("Endpoint uses plural resource names", function () {
  const url = pm.request.url.toString();

  pm.expect(url).to.match(/\/pets\/|\/orders\/|\/users\//);
  pm.expect(url).to.not.match(/\/pet\/|\/order\/|\/user\//);
});
Enter fullscreen mode Exit fullscreen mode

For a real project, scope this assertion to the endpoints where the convention applies. Authentication routes such as /auth/login are not collection resources.

Test Status Codes

Verify that creation returns 201 Created and includes a Location header:

pm.test("POST returns 201 Created", function () {
  if (pm.request.method === "POST") {
    pm.response.to.have.status(201);
    pm.response.to.have.header("Location");
  }
});
Enter fullscreen mode Exit fullscreen mode

Verify that deletion returns 204 No Content with an empty body:

pm.test("DELETE returns 204 No Content", function () {
  if (pm.request.method === "DELETE") {
    pm.response.to.have.status(204);
    pm.expect(pm.response.text()).to.be.empty;
  }
});
Enter fullscreen mode Exit fullscreen mode

Test Collection Metadata

For collection endpoints, validate the response envelope and pagination fields:

pm.test("Collection response includes pagination", function () {
  const response = pm.response.json();

  pm.expect(response).to.have.property("data");
  pm.expect(response).to.have.property("pagination");
  pm.expect(response.pagination).to.have.property("page");
  pm.expect(response.pagination).to.have.property("totalItems");
});
Enter fullscreen mode Exit fullscreen mode

Compare the Old and New Petstore APIs

Import both specifications and run the same tests against each:

  • Swagger Petstore: https://petstore.swagger.io/v2/swagger.json
  • Modern PetstoreAPI: https://petstoreapi.com/openapi.json

Then:

  1. Run the resource naming tests.
  2. Compare status code behavior.
  3. Inspect collection response structures.
  4. Review authentication requests.
  5. Compare error responses.

This makes design differences visible in endpoint definitions and runtime behavior.

Migration Guide: From Swagger Petstore Patterns to a Modern Design

If your API follows Swagger Petstore patterns, migrate incrementally where possible.

Step 1: Rename Resources Consistently

Before:

GET /pet/{petId}
POST /pet
DELETE /pet/{petId}
Enter fullscreen mode Exit fullscreen mode

After:

GET /pets/{petId}
POST /pets
DELETE /pets/{petId}
Enter fullscreen mode Exit fullscreen mode

Migration approach:

  1. Add the new plural endpoints.
  2. Continue supporting the old endpoints during the transition.
  3. Mark old endpoints as deprecated in the OpenAPI document.
  4. Update client SDKs and documentation.
  5. Monitor usage of the old endpoints.
  6. Remove them after clients have migrated.

The original migration plan recommends removing old endpoints after six months, but the appropriate timeline depends on client usage and release policy.

Step 2: Replace Action-Based Paths

Before:

GET /pet/findByStatus?status=available
GET /pet/findByTags?tags=tag1,tag2
Enter fullscreen mode Exit fullscreen mode

After:

GET /pets?status=AVAILABLE
GET /pets?tags=tag1,tag2
Enter fullscreen mode Exit fullscreen mode

Migration approach:

  1. Add query-parameter-based collection filtering.
  2. Validate allowed values such as AVAILABLE, PENDING, and SOLD.
  3. Update client SDKs.
  4. Deprecate the action-based endpoints.
  5. Redirect or remove the old endpoints according to your compatibility strategy.

Step 3: Correct HTTP Status Codes

Before:

POST /pet → 200 OK
DELETE /pet/{petId} → 200 OK with a response body
Enter fullscreen mode Exit fullscreen mode

After:

POST /pets → 201 Created with a Location header
DELETE /pets/{petId} → 204 No Content
Enter fullscreen mode Exit fullscreen mode

Changing status codes can break clients that explicitly check for 200.

Migration approach:

  1. Treat the change as a compatibility concern.
  2. Version the API if existing clients cannot handle the new responses.
  3. Document the new status codes.
  4. Update client error and success handling.
  5. Provide a migration timeline.

Step 4: Wrap Collection Responses

Before:

[
  {
    "id": 1,
    "name": "Fluffy"
  },
  {
    "id": 2,
    "name": "Buddy"
  }
]
Enter fullscreen mode Exit fullscreen mode

After:

{
  "data": [
    {
      "id": 1,
      "name": "Fluffy"
    },
    {
      "id": 2,
      "name": "Buddy"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalItems": 2,
    "totalPages": 1
  },
  "links": {
    "self": "/pets?page=1"
  }
}
Enter fullscreen mode Exit fullscreen mode

Changing an array into an object is a breaking response-shape change.

Migration approach:

  1. Introduce wrapped responses under a new API version or endpoint.
  2. Add pagination and link fields.
  3. Update client deserialization logic.
  4. Deprecate the bare-array response.
  5. Remove it after clients have migrated.

Step 5: Adopt RFC 9457 Errors

Before:

{
  "code": 400,
  "message": "Invalid input"
}
Enter fullscreen mode Exit fullscreen mode

After:

Content-Type: application/problem+json
Enter fullscreen mode Exit fullscreen mode
{
  "type": "https://petstoreapi.com/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "errors": [
    {
      "field": "name",
      "message": "Name is required",
      "code": "REQUIRED_FIELD"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Migration approach:

  1. Return application/problem+json.
  2. Define stable error types and machine-readable codes.
  3. Add field-level validation details.
  4. Update client error handling.
  5. If necessary, support old and new formats during the transition.
  6. Remove the legacy error format after migration.

Real-World Impact of Poor API Design

Developer Confusion

Inconsistent APIs force developers to spend time:

  • Guessing which HTTP method to use
  • Learning exceptions to naming conventions
  • Interpreting unexpected status codes
  • Handling unstructured errors

Client Bugs

Inconsistent response formats can cause:

  • Parsing failures
  • Authentication errors
  • Pagination defects
  • Incorrect error handling

Security Vulnerabilities

Poor endpoint design can expose:

  • Passwords in logs and browser history
  • API keys in caches and referrer headers
  • Sensitive information through error messages
  • Unauthenticated access to sensitive operations

Technical Debt

Once an anti-pattern is deployed, it becomes part of the client contract. New developers, generated SDKs, and documentation then reproduce the same design, increasing the cost of future changes.

Conclusion

Swagger Petstore remains useful as a simple OpenAPI demonstration, but it should not be treated as a complete REST API design reference. Its inconsistent naming, action-based URLs, status code usage, response structures, error format, and credential handling can lead to confusing and insecure production APIs.

Modern PetstoreAPI demonstrates an alternative approach:

  • Consistent plural resource names
  • Resource-oriented URLs
  • Semantically correct HTTP status codes
  • Collection metadata and pagination
  • RFC 9457 Problem Details
  • Standard authentication headers
  • Multiple protocol examples

Use automated checks to enforce these conventions. Import your OpenAPI specification into Apidog, test endpoint behavior, and compare the results with Modern PetstoreAPI patterns before releasing the API.

Next steps

  • Explore the Modern PetstoreAPI documentation.
  • Compare your endpoints with its resource naming patterns.
  • Import your OpenAPI specification into Apidog.
  • Add tests for status codes, response envelopes, and authentication.
  • Replace legacy errors with RFC 9457 Problem Details.
  • Plan a versioned migration for breaking response changes.

FAQ

Why did Swagger create a bad example?

Swagger Petstore was created in 2011 as a simple demonstration of the Swagger specification. It was not intended to be a complete REST API design reference. The problem is that it became the default example, so developers copied its patterns into other APIs.

Should I stop using Swagger Petstore?

Do not use it as your primary reference for REST API design. It can still demonstrate basic OpenAPI concepts, but use Modern PetstoreAPI or another intentionally designed API when learning resource modeling, status codes, error handling, and authentication patterns.

Is Modern PetstoreAPI production-ready?

Modern PetstoreAPI includes realistic business logic, authentication, rate limiting, error handling, and security features. It can be used as a reference for API design or deployed with the modifications required by a specific project.

How do I test whether my API follows REST principles?

Import your OpenAPI specification into Apidog and create automated tests for:

  • Resource naming
  • HTTP methods
  • Status codes
  • Location headers
  • Collection response structures
  • Pagination metadata
  • Error media types
  • Authentication behavior

You can also compare your API side by side with Modern PetstoreAPI.

What is the biggest mistake in Swagger Petstore?

One of the most serious issues is the login endpoint:

GET /user/login?username=john&password=secret123
Enter fullscreen mode Exit fullscreen mode

Putting passwords in URLs exposes them to browser history, server logs, proxy logs, bookmarks, and referrer headers. Use POST with a request body for authentication.

Can I migrate from Swagger Petstore patterns gradually?

Yes. Add new endpoints alongside the old ones, mark legacy endpoints as deprecated, update documentation and client SDKs, and monitor usage. Remove old endpoints only after clients have migrated according to your API compatibility policy.

Does Modern PetstoreAPI support GraphQL and gRPC?

Yes. In addition to REST, Modern PetstoreAPI supports GraphQL, gRPC, WebSocket, Server-Sent Events, MQTT, Webhooks, and Model Context Protocol. See the protocols guide for details.

How do I convince my team to improve our API design?

Demonstrate the operational cost of the current design:

  • Developers spend more time learning exceptions.
  • Clients need custom parsing and error handling.
  • Credentials may be exposed through URLs.
  • Breaking changes become harder to manage.

Use automated Apidog tests to show the violations, then compare the current API with Modern PetstoreAPI patterns and propose a migration plan.

Top comments (0)