
Let’s get real: API URLs should not look like encrypted passwords. Or like this:
/getAllUserDataNowPlease
/users/getUserById/123
/api/alphaVersionTwoPointZero/users/details?id=123If your endpoint looks like a Mad Libs generator had a panic attack, it’s time for a reset. RESTful routing is about clarity, consistency, and just enough structure.
Rule #1: Keep URLs Clean, Plural, and Resource-Centric
You’re not writing poetry. You’re exposing resources.
Good:
/users
/users/42
/posts/109/commentsBad:
/getAllUsers
/user?id=42
/commentListByPostId/109Keep it simple:
- Use lowercase
- Use plural nouns
- Use forward slashes to indicate hierarchy
Rule #2: Use Nested Routes Only When They Belong
Nested routes make sense only when one resource clearly belongs to another.
If you can’t say it out loud without sounding weird, it’s probably wrong.
Use nesting:
GET /users/42/posts ← Posts *belong* to a user
GET /orders/9/items ← Items *belong* to an orderDon’t nest just because two things are “related”:
GET /products/123/categories/9 ← Why? Category isn’t owned by product.If the resources are independent, keep them separate.
Example: Nested Resources in Code (TypeScript + Express)
// Get all posts by a user
app.get('/users/:userId/posts', (req, res) => {
const { userId } = req.params;
const posts = findPostsByUser(userId);
res.json(posts);
});
// Get a specific post by that user
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
const post = findPostByUser(userId, postId);
if (!post) return res.status(404).json({ error: 'Post not found' });
res.json(post);
});Bonus: Handling Flat Routes for Independent Resources
// Get all categories
app.get('/categories', (req, res) => {
const categories = getAllCategories();
res.json(categories);
});See how clean that is? No nesting needed — because a category isn't owned by a product.
Rule #3: Versioning Your API — Path vs Header
You’ll need API versioning eventually. Don’t overthink it.
Option A: Path versioning (simple, common)
GET /v1/usersThis is clean and self-explanatory. Works well for public APIs.
Option B: Header versioning (cleaner, but more advanced)
GET /users
Accept: application/vnd.myapp.v2+jsonGreat for internal APIs or where you want to support many versions invisibly.
Never do this:
GET /users?version=2.0.1Query params are for filtering, not for structuring your whole API lifecycle.
Recap
- Keep routes clean, lowercase, and plural
- Use nesting only for true parent-child relationships
- Version your API with paths or headers, not query strings
- Think like a user — if the URL doesn’t make sense when you read it out loud, rethink it
Coming Up Next:
Part 3: “HTTP Status Codes Aren’t Optional: Stop Using 200 for Everything”
We’ll cover the top status codes you should be using, and how to craft consistent, helpful error messages.