Suggest an editImprove this articleRefine the answer for “What is REST and REST principles — REST API”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**REST** is an architectural style for APIs where clients interact with server-managed resources via standard HTTP methods and stateless requests. Every request carries all needed context; the server keeps no session between calls. ``` GET /users - read all users POST /users - create a user (returns 201) GET /users/1 - read one user PUT /users/1 - update a user (idempotent) DELETE /users/1 - delete a user (returns 204) ``` **Key constraint:** stateless - auth tokens and pagination travel with every request, not stored on the server.Shown above the full answer for quick recall.Answer (EN)Image**REST (Representational State Transfer)** is an architectural style for networked applications where clients interact with server-managed resources using standard HTTP methods and URIs. ## Theory ### TL;DR - REST works like a public library: clients request resources by ID using standard methods (GET, POST, PUT, DELETE), and the server manages storage independently - The core constraint is statelessness: every request carries all needed context (auth token, pagination cursor), and the server keeps no memory between calls - REST fits public APIs, CRUD services, and browser-accessible resources. Skip it for real-time bidirectional communication (use WebSockets) or high-speed internal services with binary payloads (use gRPC) - REST has six constraints: client-server, stateless, cacheable, uniform interface, layered system, code-on-demand (optional) - "RESTful" means following all six. Most APIs that call themselves REST skip HATEOAS and are really just HTTP APIs ### Quick Example Basic Express.js REST API for a `/users` resource: ```javascript const express = require('express'); const app = express(); app.use(express.json()); let users = [{ id: 1, name: 'Alice' }]; app.get('/users', (req, res) => res.json(users)); // Read all app.post('/users', (req, res) => { const user = { id: users.length + 1, name: req.body.name }; users.push(user); res.status(201).json(user); // 201 Created }); app.put('/users/:id', (req, res) => { const user = users.find(u => u.id == req.params.id); if (!user) return res.status(404).json({ error: 'Not found' }); user.name = req.body.name; res.json(user); }); app.delete('/users/:id', (req, res) => { users = users.filter(u => u.id != req.params.id); res.status(204).send(); // 204 No Content }); app.listen(3000); ``` Each HTTP verb maps to exactly one CRUD operation. The server holds no session between calls. ### The Six REST Constraints Roy Fielding defined REST in his 2000 dissertation. Six constraints together produce predictable, scalable APIs. **1. Client-server.** The UI and data layer are separate. The client does not care where data lives; the server does not care how the UI renders it. **2. Stateless.** Every request contains everything the server needs: auth token, pagination cursor, content type. No session stored on the server. This is the constraint that matters most in practice. **3. Cacheable.** Responses declare whether they can be cached via `Cache-Control` or `ETag` headers. Correct caching cuts server load significantly. **4. Uniform interface.** Four sub-constraints: resources identified by URI, manipulated via representations (JSON or XML), self-descriptive messages (HTTP headers carry content type and auth), and HATEOAS (responses include links to related actions). **5. Layered system.** The client talks to one endpoint but may pass through load balancers, CDN nodes, or API gateways. Each layer sees only the adjacent one. **6. Code-on-demand (optional).** The server can send executable code to the client. Most REST APIs do not use this. Constraints 1 through 5 are the practical baseline. HATEOAS is the part most teams skip. ### Stateless vs Stateful: the Real Difference Statelessness is what makes REST scale horizontally. Any server in a cluster can handle any request because there is no shared session to worry about. The tradeoff is straightforward: requests become larger. A JWT token can be 500+ bytes. A server-side session pointer is 50 bytes. For public APIs at the scale of GitHub or Stripe, that tradeoff is worth it. For a private internal tool with two servers, it is less obvious. ### When to Use REST - Public API with many client types (browsers, mobile apps, third-party integrations): REST - Standard CRUD operations on well-defined resources: REST - Microservices where teams use different languages: REST (HTTP is universal) - Real-time bidirectional communication (chat, live notifications): WebSockets - High-performance internal services with binary payloads: gRPC - Complex queries where clients need to specify exactly what fields to fetch: GraphQL ### Protocol Comparison | Aspect | REST | SOAP | GraphQL | |---|---|---|---| | Protocol | HTTP/1.1 or HTTP/2 | HTTP, SMTP, TCP | HTTP | | Data format | JSON, XML | XML only | JSON | | State | Stateless | Stateful possible | Stateless | | Query flexibility | Fixed endpoints | XML envelope operations | Client-defined | | Request overhead | Low | High | Low to medium | | Error handling | HTTP status codes | SOAP faults | Custom errors in body | | Best for | Web APIs, browsers (Stripe) | Enterprise WS-Security (banking) | Complex queries (GitHub v4) | ### Common Mistakes In production, the most common REST mistake is not wrong HTTP verbs. It is teams breaking their own statelessness without noticing. **Storing state on the server.** The classic: `sessions[userId].cart = items`. Works on one server. Breaks when the load balancer sends the next request to a different machine. Fix: store cart state in a JWT and pass it in every request. ```javascript // Wrong: breaks with multiple servers app.post('/cart/add', (req, res) => { sessions[req.sessionId].items.push(req.body); }); // Fix: stateless approach with JWT app.post('/cart/add', (req, res) => { const cart = jwt.verify(req.headers.authorization, secret).cart; cart.items.push(req.body); res.json({ token: jwt.sign({ cart }, secret) }); }); ``` **Using POST for reads.** POST is not idempotent. If a client retries a failed POST, it may create duplicate records. Use GET for reads: it is safe and idempotent by the HTTP specification. **Returning 200 for everything.** Sending `{ error: 'Not found' }` with status 200 means monitoring tools, client error handlers, and API clients all miss the failure. Return 404, 422, 500 where they apply. **No versioning from the start.** Twitter's migration from API v1 to v2 broke thousands of integrations. Add `/api/v1/` from day one. **Treating HTTP verbs as labels.** PUT must be idempotent: calling it twice should produce the same result. PATCH updates partial state. DELETE should be safe to call twice. Breaking these contracts breaks client retry logic. ### Real-world Usage - Stripe: `GET /v1/customers/:id` returns customer data with HATEOAS links to subscriptions and charges - GitHub API: `PUT /repos/:owner/:repo` for updates, `GET /repos?page=2` for cursor pagination - Twitter API v2: `POST /2/tweets` for creating tweets with media arrays - Express.js: `app.use('/api/v1', router)` as the standard versioned route structure - React + Axios: `fetch('/api/users', { headers: { Authorization: 'Bearer token' } })` for stateless auth ### Follow-up Questions **Q:** What are the six REST constraints? **A:** Client-server, stateless, cacheable, uniform interface, layered system, code-on-demand (optional). Constraints one through five are required for a truly RESTful API. **Q:** What is HATEOAS and why do most APIs skip it? **A:** Hypermedia As The Engine Of Application State means responses include links to possible next actions. A `GET /users/1` response would contain links to edit and delete that user. Most teams skip it because it adds payload size and complexity, and clients tend to hardcode URLs anyway. **Q:** What is the difference between REST and RESTful? **A:** REST is the architectural style Fielding described. RESTful means an implementation follows all six constraints. Most APIs people call REST are really HTTP APIs that skip HATEOAS. **Q:** How do you handle authentication statelessly? **A:** With JWT Bearer tokens. The token contains user identity and is signed on the server. Every request includes it in the `Authorization` header. No session table needed. For SPAs, the OAuth2 PKCE flow handles the token exchange securely. **Q:** Design a REST API for a photo-sharing app with uploads over 2GB, cursor pagination, and real-time likes. **A:** `POST /photos` with multipart upload and resumable uploads via the Tus.io protocol. `GET /photos?cursor=abc&limit=20` for keyset pagination, which stays stable as new photos arrive. WebSockets or SSE for real-time likes since REST polling is too slow here. ETags plus a CDN layer for photo metadata caching. ## Examples ### Basic: CRUD with correct HTTP status codes ```javascript const express = require('express'); const app = express(); app.use(express.json()); let users = [{ id: 1, name: 'Alice' }]; app.get('/users', (req, res) => { res.json(users); // 200 OK by default }); app.post('/users', (req, res) => { const user = { id: users.length + 1, name: req.body.name }; users.push(user); res.status(201).json(user); // 201 Created, not 200 }); app.put('/users/:id', (req, res) => { const user = users.find(u => u.id == req.params.id); if (!user) return res.status(404).json({ error: 'Not found' }); // 404, not 200 user.name = req.body.name; res.json(user); }); app.delete('/users/:id', (req, res) => { users = users.filter(u => u.id != req.params.id); res.status(204).send(); // 204 No Content }); app.listen(3000, () => console.log('Server running on port 3000')); ``` Status codes are not decoration. `201` tells the client a resource was created. `204` signals success with no body. `404` routes to an error handler. Using `200` for all of these breaks standard tooling. ### Intermediate: Stateless pagination with Bearer token auth The client sends auth and pagination state on every request. Nothing lives on the server between calls. **Server:** ```javascript app.get('/api/v1/users', (req, res) => { const auth = req.headers.authorization?.split(' ')[1]; if (!auth) return res.status(401).json({ error: 'Unauthorized' }); const page = parseInt(req.query.page) || 1; const limit = parseInt(req.query.limit) || 10; const start = (page - 1) * limit; const paginatedUsers = users.slice(start, start + limit); res.json({ data: paginatedUsers, pagination: { page, limit, total: users.length } // Output: { data: [{ id: 1, name: 'Alice' }], pagination: { page: 1, limit: 10, total: 1 } } }); }); ``` **Client (React):** ```javascript useEffect(() => { fetch('/api/v1/users?page=1&limit=10', { headers: { Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9...' } }) .then(res => res.json()) .then(data => setUsers(data.data)); }, []); ``` The token carries user identity. The query string carries pagination state. The server had no memory of this client before this request arrived. ### Advanced: HATEOAS with ETag caching This is closer to what Fielding actually described. Responses include links to next actions; ETags let clients skip downloads when data has not changed. ```javascript app.get('/api/v1/users/:id', (req, res) => { const user = users.find(u => u.id == req.params.id); if (!user) return res.status(404).json({ error: 'Not found' }); const etag = `"${user.id}-v${user.version || 1}"`; res.set('ETag', etag); res.set('Cache-Control', 'private, max-age=60'); // 304 Not Modified if client sends a matching ETag if (req.headers['if-none-match'] === etag) { return res.status(304).send(); } res.json({ id: user.id, name: user.name, links: [ { rel: 'self', href: `/api/v1/users/${user.id}`, method: 'GET' }, { rel: 'edit', href: `/api/v1/users/${user.id}`, method: 'PUT' }, { rel: 'delete', href: `/api/v1/users/${user.id}`, method: 'DELETE' } ] }); }); ``` On the second request: ```javascript fetch('/api/v1/users/1', { headers: { 'If-None-Match': '"1-v1"' } }); // Returns 304 if nothing changed - no body, less bandwidth ``` HATEOAS means the client discovers available actions from the response itself. No need to guess what endpoints exist. Most teams adopt this selectively for resource-heavy endpoints where caching or bandwidth matters.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.