The Complete Guide to RESTful API Design — 10 Principles + Real-World Examples

Last updated: June 23, 2026 · 12 min read · Category: Backend Engineering

✍️ Author:DevToolbox Team📅 Updated:2026-06-24📎 References:RFC Standards

📌 Key Takeaways

  • The Complete Guide to RESTful API Design — 10 Prin is widely used by developers
  • Based on RFC standards and real-world experience
  • Free online tools, runs locally, no data upload
  • FAQ section at the bottom answers common questions

RESTful API design is the cornerstone of every modern backend system. Whether you're building a small service or a public-facing platform, following a consistent set of RESTful best practices means fewer surprises, faster onboarding, and APIs that age well. This guide distills the 10 core principles, walks through a real Node.js/Express example, lists the 10 most common anti-patterns, and ends with an in-depth REST vs GraphQL comparison.

1. What Is REST? Why Does Design Quality Matter?

REST (Representational State Transfer) was proposed by Roy Fielding in his 2000 doctoral dissertation. Its core idea is simple: treat the URL as a resource identifier, and use HTTP verbs to express actions on that resource. The data format is usually JSON, and every resource is uniquely addressable.

Why does RESTful API design quality matter so much? Three reasons:

Before diving into principles, let's lock in the API design principles we'll be using throughout. They are also the reason this article can serve as a permanent reference for your team.

2. The 10 RESTful Best Practices

Principle 1: URLs are resources, verbs live in HTTP methods

The most common mistake beginners make is putting verbs in the URL:

GET    /users/123      # Fetch user with id=123
POST   /users          # Create a user
PUT    /users/123      # Update user with id=123
DELETE /users/123      # Delete user with id=123

Once you move verbs from the URL into HTTP methods, your API becomes self-describing — anyone can read the intent from the URL + Method without checking the docs.

Principle 2: Use plural resource names — keep collections and items consistent

Resource collections should use plural nouns, so that "lists" and "single items" stay stylistically consistent in URLs. Even if a resource is logically always a singleton (such as /settings), it's still recommended to keep it plural so future extensions don't force a breaking rename.

GET /user/123 → ✅ GET /users/123

GET /article/5 → ✅ GET /articles/5

Plural naming also makes it easy to add batch operations (POST /users/batch-delete) and aggregated resources (GET /users/active) later.

Principle 3: Express intent with HTTP verbs — 5 methods cover CRUD

RESTful API design principles require strict use of HTTP verbs to express semantics — this is the heart of the uniform interface. The semantics of the 5 common verbs:

Example:

GET    /articles           # List
GET    /articles/42        # Detail
POST   /articles           # Create
PUT    /articles/42        # Full replace
PATCH  /articles/42        # Partial update
DELETE /articles/42        # Delete

Principle 4: Use HTTP status codes precisely — never "all 200"

HTTP status codes are the first signal a client uses to judge a request's outcome. Stuffing business errors into a 200 is the most common anti-pattern — it forces the frontend to parse the body just to find out whether the request succeeded. Use them as follows:

Using the right status code is the most overlooked yet highest-leverage step in API design conventions.

Principle 5: Versioning — leave room for evolution from day one

APIs will change, but already-published endpoints should never be silently broken. The most robust engineering practice is to put the version number in the URL path (e.g. /api/v1/users) rather than burying it in a header or query string — the path is friendliest to humans, to CDN/gateway routing, and to search engines.

Comparison of three mainstream versioning approaches:

A new version should run in parallel for at least 6–12 months, giving clients a comfortable migration window.

Principle 6: Use JSON, not XML — but keep Content-Type negotiation

As of 2026, JSON is the de facto standard: small payload, highly readable, natively compatible with JavaScript. Unless your client requires XML (such as certain financial or government systems), always use Content-Type: application/json; charset=utf-8.

Response example:

{
  "id": 123,
  "name": "Alice",
  "email": "[email protected]",
  "created_at": "2026-06-08T10:00:00Z"
}

Always explicitly declare Content-Type in the response — don't make the client guess.

Principle 7: Filter, sort, paginate — standard for collection endpoints

Once a resource collection can grow large, you must support filtering, sorting, and pagination. Query strings are recommended:

GET /api/v1/orders?status=active&sort=-created_at&page=1&limit=20
GET /api/v1/orders?created_after=2026-01-01&search=phone
GET /api/v1/orders?fields=id,total,status    # Sparse fieldsets

Three common pagination strategies:

It's also recommended to include pagination metadata in the response body:

{
  "data": [ ... ],
  "meta": { "page": 1, "limit": 20, "total": 1024, "has_more": true }
}

Principle 8: Express nested resources with path hierarchy

When a resource naturally belongs to another, use path nesting to express the relationship:

GET    /users/123/orders            # All orders of user 123
GET    /users/123/orders/456        # Order 456 of user 123
POST   /users/123/orders            # Create an order for user 123
GET    /articles/42/comments        # Comments on article 42

But note: nesting should not exceed 3 levels, otherwise the URL becomes hard to maintain. Beyond 3 levels, switch to a top-level resource with filtering:

GET /orders?user_id=123             # Better than /users/123/orders/recent/top-5

Principle 9: Unify error response format — make errors machine-readable

A good error response should be both human-readable and machine-readable. A unified structure is recommended:

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "User with id=999 does not exist",
    "details": {
      "resource": "user",
      "id": 999
    },
    "request_id": "req_8f3a2b"
  }
}

Key takeaways:

Principle 10: Security — HTTPS, CORS, rate limiting, and authentication are all mandatory

The API is the "front door" of your system — security is non-negotiable. Four must-haves:

3. Hands-On Example — A Todo API with Node.js/Express

Theory's done — time for code. Below is a production-grade, minimum-viable Todo API covering all 5 endpoints (GET/POST/PUT/PATCH/DELETE) plus JWT auth, a unified error format, and rate limiting. Just copy and run:

// app.js
import express from 'express';
import jwt from 'jsonwebtoken';
import rateLimit from 'express-rate-limit';

const app = express();
app.use(express.json());
app.use(rateLimit({ windowMs: 60_000, max: 100 })); // 100 req/min/IP

const SECRET = process.env.JWT_SECRET || 'dev-secret-change-me';
let todos = [{ id: 1, title: 'Learn REST', done: false }];
let nextId = 2;

// ----- Unified error middleware -----
const errorHandler = (err, req, res, next) => {
  const status = err.status || 500;
  res.status(status).json({
    error: {
      code: err.code || 'INTERNAL_ERROR',
      message: err.message || 'Server Error',
      request_id: req.id
    }
  });
};

// ----- JWT auth middleware -----
const auth = (req, res, next) => {
  const token = (req.headers.authorization || '').replace('Bearer ', '');
  try { req.user = jwt.verify(token, SECRET); next(); }
  catch { next({ status: 401, code: 'UNAUTHORIZED', message: 'Invalid or missing token' }); }
};

// ----- 5 RESTful endpoints -----
app.get   ('/api/v1/todos',        auth, (req, res) => res.json({ data: todos }));
app.get   ('/api/v1/todos/:id',    auth, (req, res) => {
  const t = todos.find(x => x.id === +req.params.id);
  if (!t) return next({ status: 404, code: 'TODO_NOT_FOUND', message: 'Todo not found' });
  res.json({ data: t });
});
app.post  ('/api/v1/todos',        auth, (req, res) => {
  const t = { id: nextId++, title: req.body.title, done: false };
  todos.push(t);
  res.status(201).json({ data: t });
});
app.put   ('/api/v1/todos/:id',    auth, (req, res) => {
  const t = todos.find(x => x.id === +req.params.id);
  if (!t) return next({ status: 404, code: 'TODO_NOT_FOUND', message: 'Todo not found' });
  Object.assign(t, req.body);  // Full replace
  res.json({ data: t });
});
app.patch ('/api/v1/todos/:id',    auth, (req, res) => {
  const t = todos.find(x => x.id === +req.params.id);
  if (!t) return next({ status: 404, code: 'TODO_NOT_FOUND', message: 'Todo not found' });
  Object.assign(t, req.body);  // Partial update
  res.json({ data: t });
});
app.delete('/api/v1/todos/:id',    auth, (req, res) => {
  todos = todos.filter(x => x.id !== +req.params.id);
  res.status(204).end();
});

app.use(errorHandler);
app.listen(3000);

Test requests (using curl):

# Log in to get a token
curl -X POST http://localhost:3000/login -d '{"user":"alice","pass":"x"}' -H 'Content-Type: application/json'
# Create a todo
curl -X POST http://localhost:3000/api/v1/todos -H "Authorization: Bearer *** -H 'Content-Type: application/json' -d '{"title":"Finish the RESTful tutorial"}'
# Partial update (PATCH)
curl -X PATCH http://localhost:3000/api/v1/todos/1 -H "Authorization: Bearer *** -H 'Content-Type: application/json' -d '{"done":true}'

In roughly 60 lines of code you've fully covered the 10 core principles of RESTful API design.

4. 10 Most Common RESTful Anti-Patterns

With the good practices covered, let's call out the bad ones. These 10 errors appear most often in code reviews:

  1. Verbs in the URL: /getUsers, /createOrder — violates "the URL is a resource, not an action."
  2. Returning 200 for everything: Stuffing business errors into { success: false, code: 1 } forces the frontend to parse the body.
  3. Not distinguishing POST / PUT / PATCH: Using POST for create, full update, and partial update breaks idempotency.
  4. No versioning: Shipping breaking field changes on /api/users overnight brings clients down with you.
  5. No pagination: GET /users returns 100k rows at once — an OOM is only a matter of time.
  6. Inconsistent error shapes: Sometimes msg, sometimes message, sometimes error — the frontend ends up writing a wall of if-else.
  7. Ignoring HTTP caching: GET endpoints don't return ETag / Cache-Control, wasting CDN capacity for nothing.
  8. Mixed field-naming conventions: userId, user_id, userID all in the same API — pick camelCase or snake_case and stick with it project-wide.
  9. Mapping every error to 500: Bad parameters, missing permissions, missing resources all return 500 — the logs turn into a sea of red and the real problem gets buried.
  10. Plain HTTP + no authentication: Production still runs on http://api.xxx.com, the login endpoint has no HTTPS, tokens travel in the clear.

Avoid these 10 pitfalls and your API will be solid and professional in 95% of scenarios.

5. RESTful vs GraphQL — How to Choose?

Discussing the REST vs GraphQL difference is almost a rite of passage in backend interviews. The two aren't about "one replacing the other" — they fit different scenarios:

A one-line rule of thumb: general-purpose, stable workloads, heavy caching → REST; many clients, heavy aggregation, fast iteration → GraphQL. Small and mid-sized teams should default to REST and reach for GraphQL only when the scenario demands it. The two can also coexist — many large systems use REST as the public gateway and GraphQL as the internal aggregation layer.

6. Conclusion

Mastering the 10 RESTful API design principles above covers 95% of the APIs you'll build in your career. Always remember: URL is the resource, verbs live in HTTP methods, status codes are the first signal, error format must be unified, and security is non-negotiable. A great API is one a new developer can understand within 5 minutes — that is the true measure of API design principles.

For quick lookups during your day-to-day work, try the related tools in our toolbox:

🔗 Share: 𝕏 📘 ✈️ 💬

FAQ: Common Questions

Q: RESTful API 是什么?

REST(Representational State Transfer)是 Roy Fielding 2000 年提出的 API 设计风格。用 HTTP 方法(GET/POST/PUT/DELETE)+ URL 路径表示资源操作。

Q: URL 应该是名词还是动词?

RESTful URL 用名词(资源):`/users/123/orders`(获取用户 123 的订单)。动词放在 HTTP 方法里:`GET` 查询、`POST` 创建。

Q: REST 和 GraphQL 怎么选?

REST 简单、缓存友好、HTTP 标准化,适合大多数 CRUD 场景。GraphQL 灵活、前端可定制查询,适合复杂关联数据、移动端。

🧰
Add to Home Screen
Works offline, launches instantly