Tools
Tools: The Backend Setup Every Developer Should Follow
2026-03-03
0 views
admin
Routes – The Entry Points ## Controllers – The Request Handlers ## Middleware – The Gatekeepers ## Services – The Brain of the Application ## Repository – The Data Access Layer ## Database – The Storage Engine ## Full Request Flow ## 🧠 Simple Mental Model Before you write your backend code, it is very important to have a clean and organized setup — one that helps you scale, debug, and collaborate better. There are 6 major layers of it. Routes define which function should run when a specific request hits your server. They map HTTP methods and URLs to controllers. Routes should be thin and clean.
They don’t contain logic.
They only decide where the request goes. Think of Routes as:
The road map of your backend. Think of Controllers as:
The receptionist. They receive and respond. Middleware runs before the controller. The request must pass through middleware before reaching the controller. Think of Middleware as:
The security check at the airport. This is where your real logic lives. Orchestration of multiple repositories. Think of Services as:
The decision-maker. The repository is responsible for: An abstraction over the database.
If tomorrow you switch from PostgreSQL to MongoDB,
only the repository layer should change. Think of Repository as:
The translator between your app and the database. It does not care about: Database → Repository → Service → Controller → Client Clean. Predictable. Scalable. If you're ever confused where something belongs, ask: Does it handle HTTP? → Controller Does it validate/authenticate/log? → Middleware Is it business logic? → Service Is it database query? → Repository Is it storage? → Database Does it map endpoint to controller? → Route This structure is inspired by architectural principles popularized in
Clean Architecture by Robert C. Martin — but simplified for practical backend applications. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
router.post("/signup", userController.signup);
router.get("/profile", authMiddleware, userController.getProfile); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
router.post("/signup", userController.signup);
router.get("/profile", authMiddleware, userController.getProfile); CODE_BLOCK:
router.post("/signup", userController.signup);
router.get("/profile", authMiddleware, userController.getProfile); CODE_BLOCK:
router.get("/profile", authMiddleware, userController.getProfile); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
router.get("/profile", authMiddleware, userController.getProfile); CODE_BLOCK:
router.get("/profile", authMiddleware, userController.getProfile); COMMAND_BLOCK:
exports.signup = async ({ email, password }) => { const existingUser = await userRepository.findByEmail(email); if (existingUser) { throw new Error("User already exists"); } const hashedPassword = await bcrypt.hash(password, 10); return await userRepository.createUser(email, hashedPassword);
}; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
exports.signup = async ({ email, password }) => { const existingUser = await userRepository.findByEmail(email); if (existingUser) { throw new Error("User already exists"); } const hashedPassword = await bcrypt.hash(password, 10); return await userRepository.createUser(email, hashedPassword);
}; COMMAND_BLOCK:
exports.signup = async ({ email, password }) => { const existingUser = await userRepository.findByEmail(email); if (existingUser) { throw new Error("User already exists"); } const hashedPassword = await bcrypt.hash(password, 10); return await userRepository.createUser(email, hashedPassword);
}; COMMAND_BLOCK:
exports.findByEmail = async (email) => { return db("users").where({ email }).first();
}; Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
exports.findByEmail = async (email) => { return db("users").where({ email }).first();
}; COMMAND_BLOCK:
exports.findByEmail = async (email) => { return db("users").where({ email }).first();
}; - Controllers - Reading request data (req.body, req.params, req.query)
- Sending response (res.json, res.status)
- Calling the service layer - Contain business logic
- Talk directly to the database
- Hash passwords
- Calculate business rules - Authentication (JWT verification)
- Input validation
- Rate limiting
- Error handling - Business rules
- Data transformations
- Workflow decisions - Communicating with the database
- Executing queries
- Returning raw data - Decide business rules
- Validate logic
- Handle HTTP - Retrieve data
- Maintain integrity - Business logic
- Application rules
- It only stores information - Let’s say a user signs up.
- Request hits Route
- Middleware validates token/input
- Controller receives request
- Controller calls Service
- Service applies business logic
- Service calls Repository
- Repository talks to Database
- Response travels back up the chain
how-totutorialguidedev.toaiserverrouterswitchpostgresqldatabase