API Design Patterns for Modern Web Applications

Esther Howard's avatar

João Castro

blog-details-cover

Introduction

An API is the contract between your frontend and backend. When that contract is well-designed, development is smooth, bugs are rare, and the frontend team can work independently from the backend team. When the API is poorly designed, every feature becomes a coordination problem, error handling is inconsistent, and performance issues are hard to diagnose.

Whether you are designing an API manually or reviewing one generated by AI, understanding established patterns saves time and prevents common mistakes.

RESTful Resource Design

REST APIs organize around resources -- nouns, not verbs. A resource is a concept your application manages: users, projects, tasks, orders. Each resource gets a predictable set of endpoints:

  • GET /api/users -- List all users (with pagination)
  • GET /api/users/:id -- Get a single user
  • POST /api/users -- Create a new user
  • PUT /api/users/:id -- Update an existing user
  • DELETE /api/users/:id -- Delete a user

This convention means anyone familiar with REST can immediately understand your API without reading documentation. Nested resources express relationships: GET /api/projects/:id/tasks returns all tasks belonging to a project.

HTTP Status Codes

Using the correct HTTP status code is one of the simplest ways to make an API developer-friendly. The most important codes to use correctly:

  • 200 OK: Successful read or update
  • 201 Created: Successful resource creation
  • 204 No Content: Successful deletion
  • 400 Bad Request: Client sent invalid data (include details in the response body)
  • 401 Unauthorized: Authentication required or token expired
  • 403 Forbidden: Authenticated but not authorized for this resource
  • 404 Not Found: Resource does not exist
  • 409 Conflict: Request conflicts with current state (e.g., duplicate email)
  • 500 Internal Server Error: Something broke on the server

APIs that return 200 for everything and put the real status in the response body are harder to work with because generic error handling cannot distinguish success from failure.

Pagination

Any endpoint that returns a list of resources needs pagination. Without it, a table with 100,000 records will return all of them in a single response, crushing both the server and the client. The two most common pagination strategies are offset-based and cursor-based.

Offset-based pagination uses ?page=2&limit=20 parameters. It is simple to implement and understand, and works well for datasets that do not change frequently. The downside is that inserting or deleting records can cause items to shift between pages.

Cursor-based pagination uses a cursor (typically the ID of the last item) to fetch the next batch: ?after=abc123&limit=20. It is more reliable for real-time data because it is not affected by insertions or deletions, but it does not support jumping to a specific page number.

Error Response Format

Consistent error responses make debugging faster. A good error response includes: an error code (machine-readable), a message (human-readable), and optionally the specific field that caused the error. For example:

{
  "error": "VALIDATION_ERROR",
  "message": "Email address is invalid",
  "field": "email"
}

For endpoints that validate multiple fields, return all errors at once rather than one at a time. This saves the client from submitting the form repeatedly to discover each error.

Authentication Patterns

JWT (JSON Web Tokens) is the most common authentication pattern for modern web APIs. The client sends credentials (email and password), receives a token, and includes that token in the Authorization header of subsequent requests. The server validates the token without needing to query a session store.

For applications that need revocation (logging out a user immediately), combine JWTs with a short expiration time and a refresh token stored in an HTTP-only cookie. The access token expires quickly (15-60 minutes), and the refresh token is used to get a new access token without requiring the user to log in again.

API design patterns

Key Principles

  • Use consistent naming conventions (camelCase or snake_case, but not both)
  • Version your API from the start (/api/v1/users) to allow future changes
  • Include pagination on all list endpoints
  • Return consistent error formats across all endpoints
  • Use proper HTTP methods and status codes
  • Document your API, even if the documentation is auto-generated

Conclusion

Good API design is an investment that pays dividends throughout a project's lifecycle. It makes frontend development faster, debugging easier, and third-party integrations smoother. Whether you are designing an API from scratch or working with one generated by AI tools, these patterns provide a reliable foundation.

The best APIs are invisible -- they work exactly as you expect, every time.

Share this post
Comments
Esther Howard's avatar

Esther Howard

Until recently, the prevailing view assumed lorem ipsum was born as a nonsense text. It's not Latin though it looks like nothing.

Reply

Get product updates and tips

New features, AI model updates, and building tips — straight to your inbox.

  • No spam, ever

  • Unsubscribe anytime

  • Product updates & tips