10 Common Mistakes When Building REST APIs (and How to Avoid Them)

REST APIs seem simple-until you try building a real one. Whether you’re designing internal services or public endpoints, mistakes in structure, naming, or error handling can haunt you for months.
Photo by Francisco De Legarreta C. on Unsplash
Read this first
- Mistakes are common everywhere. Don’t fret it too much. Never stop learning.
Here are 10 common (but fixable) mistakes developers make when building REST APIs-and what to do instead.
1. Using Verbs in Endpoints
REST is resource-oriented. The HTTP method already describes the action. If you are using verbs to define your endpoint, it will be redundant.
GET /createUser
POST /deleteOrder
You can fix this by using nouns for endpoints and verbs only in HTTP methods.
POST /users // create user
DELETE /orders/:id // delete order
2. Ignoring Status Codes
This confuses clients that rely on status codes for logic (think: frontend, mobile apps, API consumers).
res.status(200).json({ error: "User not found" });
Utilize the status code based on their meaning. Return appropriate status codes-200, 201, 400, 404, 500... Don't make everything a 200.
res.status(404).json({ error: "User not found" });
3. Exposing Internal Errors Directly
From security point of view, it could leak some sensitive information or give clues to intruders.
res.status(500).json({ message: err.message }); // might expose stack traces or DB internals
Carefully review your implementation. Log errors internally if required.
res.status(500).json({ error: "Internal server error" });
myLogger.logError(err); // log the detailed error internally
4. Not Versioning Your API
Excluding version route in your endpoint can lead to potential future issues
GET /users
// How do you release breaking changes later?
A simple way to fix it would be include version info in your endpoint. Make this habit from day one.
GET /v1/users
Alternatively, you could use headers (Accept-Version).
5. Overloading 200 OK for Everything
This sounds familiar to an earlier mistake. Using 200 everytime might be syntactically OK but it gives a false confidence to clients.
res.status(200).json({ success: false, error: "Bad input" });
Be semantically correct as well. Don’t lie with 200.
res.status(400).json({ error: "Bad input" });
6. Inconsistent Naming and Pluralization
GET /user
GET /orders
POST /order
There’s no strict rule for it but assume plural noun to be the norm to make your life (and your future-self’s life) easier.
GET /users
GET /orders
POST /orders
7. Cramming Too Much Into One Endpoint
The most common reason for an endpoint to grow insanely huge-it’s convenient.
GET /users?includeOrders=true&includeComments=true&includeProfile=true
There are many ways to fix this. Let’s go with a basic idea of breaking down the endpoint by resources.
GET `/users/:id`
GET `/users/:id/orders`
GET `/users/:id/profile`
8. No Rate Limiting or Throttling
A classic but costly mistake. Anyone can hit your API a few thousand times a second and you won’t notice until the server dies. (The intention is not always with a DDoS intent… but it could be!)
Always rate-limit your APIs.
// Simple example with middleware
import rateLimit from 'express-rate-limit';
app.use(rateLimit({
windowMs: 1 * 60 * 1000,
max: 100,
}));
9. Trusting the Client a bit too much (Poor Input Validation)
Clients don’t always send what you expect-sometimes due to bugs, sometimes due to… creative users. This leads to garbage data, crashes, or worse-security issues.
app.post('/users', (req, res) => {
const { email, age } = req.body; // assumes everything's fine
// ...
});
Always validate your input. There are various libraries to make it easy for you — Zod, Yup, Joi, etc.
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
age: z.number().int().positive(),
});
app.post('/users', (req, res) => {
const parsed = schema.safeParse(req.body);
if (!parsed.success) return res.status(400).json({ error: parsed.error });
// ...
});
10. Assuming Everyone Speaks JSON
Most APIs default to JSON responses-and that’s fine. But assuming it’s the only format clients ever need can be shortsighted.
res.send('Success'); // No content type set
Or:
res.json({ message: "Success" }); // even when the client asked for CSV or XML
You don’t negotiate the correct
Content-Type, so clients may misinterpret your response.Some clients (like CLI tools, legacy systems, BI tools) might expect other formats like CSV or XML.
Debugging becomes painful when the frontend receives HTML error pages instead of structured JSON errors.
I think requesting something apart from JSON is a rare problem but let’s consider a possible fix.
Utilize Accept and Content-Type headers. Check and respect the Accept header sent by the client. Here's a TypeScript + Express example that supports both application/json and text/csv:
app.get('/reports', (req, res) => {
const data = [
{ id: 1, name: "Hakuna" },
{ id: 2, name: "Matata" },
];
const accept = req.headers['accept'];
if (accept?.includes('text/csv')) {
const csv = data.map(row => `${row.id},${row.name}`).join('\n');
res.setHeader('Content-Type', 'text/csv');
return res.status(200).send(csv);
}
if (accept?.includes('application/json') || !accept) {
res.setHeader('Content-Type', 'application/json');
return res.status(200).json(data);
}
return res.status(406).send('Not Acceptable');
});
Use the
Acceptheader to determine what the client wants.Return a
406 Not Acceptableif the requested format isn't supported.Communicate data type on both sides with
Content-Typeheader.Keep JSON as your default, but make future extensibility easy.
One Last Thing: Undocumented APIs Are Invisible
If no one knows how to use your API, it might as well not exist.
Use tools like OpenAPI, Swagger UI, or Redoc to generate and maintain clear, up-to-date docs automatically.
Final Thoughts
Designing a good REST API isn’t just about writing endpoints-it’s about building clear, predictable contracts between systems. These mistakes are common, but fixing them early can save you from weeks of work later.
Start simple. Make things explicit. Keep improving.
Have you stumbled on any of these? Drop a comment-I’d love to hear it.



