RESTful Messages & Status Codes — Request/Response Guide
RESTful messages are HTTP request and response structures carrying data between clients and servers using headers, status codes, and bodies.
What You’ll Learn
- The structure of HTTP requests and responses in REST
- Common request headers for content type, auth, and caching
- Response status codes by category (1xx–5xx)
- Content negotiation and format selection
- Consistent error response patterns
Why Messages Matter
Every API interaction is a conversation. The client sends a request saying what it wants, and the server responds with the result. How you format these messages determines whether developers understand your API in minutes or struggle for hours. DodaTech’s Durga Antivirus Pro API uses consistent message patterns — every response includes the same error structure, every request uses standard headers, and status codes precisely describe what happened.
sequenceDiagram
participant Client
participant Server
Client->>Server: GET /api/v1/threats<br/>Accept: application/json<br/>Authorization: Bearer tok
Server->>Client: 200 OK<br/>Content-Type: application/json<br/>Cache-Control: max-age=300<br/>[{"id":"th-001","name":"Emotet"}]
Client->>Server: POST /api/v1/threats<br/>Content-Type: application/json<br/>{"name":"NewThreat","hash":"..."}
Server->>Client: 201 Created<br/>Location: /api/v1/threats/th-003<br/>{"id":"th-003",...}
Note over Client,Server: Status codes tell the story
HTTP Request Structure
Every HTTP request has three parts:
- Request line: Method, URI, HTTP version
- Headers: Metadata about the request
- Body: Data (for POST, PUT, PATCH only)
POST /api/v1/threats HTTP/1.1
Host: api.durga-antivirus.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
Accept: application/json
User-Agent: Durga-Antivirus/2.1
{"name": "Trojan.Generic", "hash": "a1b2c3d4", "severity": "high"}Common Request Headers
| Header | Purpose | Example |
|---|---|---|
Authorization | Authentication credentials | Bearer eyJhbGci... |
Content-Type | Format of the request body | application/json |
Accept | Desired response format | application/json |
User-Agent | Client identification | Durga-Antivirus/2.1 |
Cache-Control | Caching preferences | no-cache |
If-None-Match | Conditional request (ETag) | "abc123" |
Why Content-Type and Accept are separate: Content-Type tells the server how to parse the body you’re sending. Accept tells the server what format you want the response in. They can be different — you might send form data (application/x-www-form-urlencoded) but want JSON back (application/json).
HTTP Response Structure
Every response also has three parts:
- Status line: HTTP version, status code, reason phrase
- Headers: Metadata about the response
- Body: Response data
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=300
ETag: "abc123"
X-Request-Id: req-789
{"id": "th-001", "name": "Emotet", "severity": "critical"}Status Codes by Category
Status codes are grouped into five classes. The first digit tells you the category:
2xx — Success
| Code | Meaning | When to Use |
|---|---|---|
200 OK | Success | GET, PUT, PATCH, DELETE (with body) |
201 Created | Resource created | POST — include Location header |
202 Accepted | Accepted for processing | Async operations (file scan queued) |
204 No Content | Success, no body | DELETE, some PUT operations |
3xx — Redirection
| Code | Meaning | When to Use |
|---|---|---|
301 Moved Permanently | Resource moved | API endpoint changed permanently |
304 Not Modified | Use cached version | Conditional GETs with ETag |
4xx — Client Error
| Code | Meaning | When to Use |
|---|---|---|
400 Bad Request | Malformed syntax | Invalid JSON, missing required fields |
401 Unauthorized | Authentication required | Missing or invalid API key |
403 Forbidden | Insufficient permissions | Valid auth but no access |
404 Not Found | Resource doesn’t exist | Invalid ID or URI |
405 Method Not Allowed | Wrong HTTP method | POST on read-only endpoint |
409 Conflict | Resource state conflict | Duplicate creation, stale version |
422 Unprocessable Entity | Semantic validation error | Email format invalid |
429 Too Many Requests | Rate limit exceeded | Too many requests in time window |
5xx — Server Error
| Code | Meaning | When to Use |
|---|---|---|
500 Internal Server Error | Unexpected error | Unhandled exception |
502 Bad Gateway | Upstream failure | Downstream service unavailable |
503 Service Unavailable | Server overloaded | Maintenance mode, rate limiting at infrastructure |
Content Negotiation
Content negotiation is how the client and server agree on the format for data exchange. The client sends an Accept header, and the server responds with the best matching format.
// Client requests JSON
GET /api/v1/threats HTTP/1.1
Accept: application/json
// Server responds with JSON
HTTP/1.1 200 OK
Content-Type: application/json
...
// Client requests XML
GET /api/v1/threats HTTP/1.1
Accept: application/xml
// Server responds with XML (if supported)
HTTP/1.1 200 OK
Content-Type: application/xml
...Consistent Error Responses
Every error should follow the same structure so clients can parse errors programmatically:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request body contains invalid fields.",
"details": [
{ "field": "email", "message": "Must be a valid email address" },
{ "field": "age", "message": "Must be a positive integer" }
],
"requestId": "req-789"
}
}Why requestId? When something goes wrong, developers need to trace the issue. Including a unique request ID in every response (and logging it server-side) lets you correlate error reports with server logs without exposing internal details.
Common Mistakes
1. Returning 200 for All Successful Requests
Using 200 for everything (creation, deletion, successful GET) loses the semantic meaning of status codes. 201 says “resource created” — it’s more informative than 200.
2. Inconsistent Error Body Formats
One endpoint returns {"error": "not found"}, another returns {"code": 404, "msg": "Resource not found"}. This forces clients to write custom error parsers for each endpoint. Standardize the error format across your entire API.
3. Exposing Stack Traces in 500 Errors
Returning a stack trace or internal error message (database credentials, file paths) in production is a security vulnerability. Always return a generic 500 Internal Server Error with a request ID for tracking.
4. Ignoring the Location Header After POST
The Location header in a 201 Created response tells the client where the new resource lives. Without it, the client must guess the URI or parse the response body. This breaks HATEOAS principles.
5. Using Wrong 4xx Code
400 Bad Request is for malformed syntax (invalid JSON). 422 Unprocessable Entity is for semantic validation failures (email format wrong). Using them interchangeably confuses clients.
Practice Questions
- What are the five categories of HTTP status codes?
- What does
201 Createdinclude that200 OKdoesn’t? - What is the difference between
400 Bad Requestand422 Unprocessable Entity? - Why should every response include a request ID?
- What is the purpose of the
Acceptheader?
Answers:
- 1xx (Informational), 2xx (Success), 3xx (Redirection), 4xx (Client Error), 5xx (Server Error).
- A
Locationheader pointing to the URI of the newly created resource. 400is for malformed syntax (invalid JSON, wrong data type).422is for valid syntax but invalid semantics (well-formed JSON with invalid email format).- Request IDs let developers correlate error reports with server logs to diagnose issues without exposing internal details.
- The
Acceptheader tells the server what response format the client prefers (JSON, XML, etc.).
Challenge: Write a complete HTTP response for POST /api/v1/devices that fails with a validation error. Include the status code, headers, and a standardized error body with at least two field-level errors.
FAQ
Try It Yourself
Create a mock API that returns proper status codes using this Node.js Express server:
const express = require('express');
const app = express();
app.use(express.json());
const threats = [
{ id: 'th-001', name: 'Emotet', severity: 'critical' }
];
// GET — list threats
app.get('/api/v1/threats', (req, res) => {
res.status(200).json(threats);
});
// POST — create threat (validation example)
app.post('/api/v1/threats', (req, res) => {
if (!req.body.name) {
return res.status(422).json({
error: { code: 'VALIDATION_ERROR', message: 'Name is required' }
});
}
const threat = { id: `th-${Date.now()}`, ...req.body };
threats.push(threat);
res.status(201).location(`/api/v1/threats/${threat.id}`).json(threat);
});
app.listen(3000);Run this with node server.js and test with curl to see proper status codes in action.
What’s Next
| Topic | Description |
|---|---|
| Statelessness & Caching in REST | How stateless design enables scaling and caching |
| REST API Security | Authentication, authorization, and rate limiting |
| RESTful APIs Overview | Review the complete REST architecture |
| GraphQL vs REST | Compare message patterns across API styles |
What’s Next
Congratulations on completing this Restful Messages tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro