When building a scalable backend API, one common question is:
Should we separate admin routes like /api/admin/categories
Or protect routes using middleware?
In this article, I’ll show you the clean and scalable way to structure public and admin routes.
🚀 The Use Case
We are building a Blog System with categories.
We need:
Public users → View all categories
Admin users → Create categories
❌ Bad Practice (URL-Based Role Separation)
POST /api/admin/categories
GET /api/categories
Why this is not ideal?
URL becomes tightly coupled with roles
Harder to scale
Not RESTful
Role logic leaks into routing
✅ Recommended Approach (Middleware-Based Authorization)
Keep URLs clean and RESTful.
GET /api/categories → Public
POST /api/categories → Admin only
Access control should be handled by middleware, not the URL.
📂 Project Structure (Feature-Based Architecture)
src/
├── modules/
│ ├── categories/
│ │ ├── categories.controller.ts
│ │ ├── categories.service.ts
│ │ ├── categories.routes.ts
│ │ ├── categories.model.ts
│
├── middleware/
│ ├── auth.middleware.ts
│ ├── admin.middleware.ts
This structure keeps everything modular and scalable.
🛣 categories.routes.ts
import { Router } from "express";
import { CategoriesController } from "./categories.controller";
import { authMiddleware } from "../../middleware/auth.middleware";
import { adminMiddleware } from "../../middleware/admin.middleware";
const router = Router();
/* ---------- PUBLIC ROUTE ---------- */
router.get("/", CategoriesController.getAll);
/* ---------- ADMIN ROUTE ---------- */
router.post(
"/",
authMiddleware,
adminMiddleware,
CategoriesController.create
);
export default router;
🔐 Why Middleware-Based Authorization is Better
✅ Clean RESTful design
✅ Scalable for future roles (editor, moderator, etc.)
✅ Separation of concerns
✅ Professional backend structure
🏆 Final API Design
Method Endpoint Access
GET /api/categories Public
POST /api/categories Admin
PUT /api/categories/:id Admin
DELETE /api/categories/:id Admin
Top comments (2)
wow Karthick
Thank you