The Backend Setup Every Developer Should Follow
Source: Dev.to

Introduction
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
- Controllers
- Middleware
- Services
- Repository
- Database
Routes – The Entry Points
Routes define which function should run when a specific request hits your server. They map HTTP methods and URLs to controllers.
router.post("/signup", userController.signup);
router.get("/profile", authMiddleware, userController.getProfile);
Routes should be thin and clean. They don’t contain logic; they only decide where the request goes. Think of routes as the roadmap of your backend.
Controllers – The Request Handlers
Controllers handle:
- Reading request data (
req.body,req.params,req.query) - Sending response (
res.json,res.status) - Calling the service layer
They should NOT:
- Contain business logic
- Talk directly to the database
- Hash passwords
- Calculate business rules
Think of controllers as the receptionist: they receive and respond.
Middleware – The Gatekeepers
Middleware runs before the controller. It is used for:
- Authentication (JWT verification)
- Logging
- Input validation
- Rate limiting
- Error handling
router.get("/profile", authMiddleware, userController.getProfile);
The request must pass through middleware before reaching the controller. Think of middleware as the security check at the airport.
Services – The Brain of the Application
This is where your real logic lives. Services handle:
- Business rules
- Data transformations
- Workflow decisions
- Orchestration of multiple repositories
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);
};
Think of services as the decision‑maker.
Repository – The Data Access Layer
The repository is responsible for:
- Communicating with the database
- Executing queries
- Returning raw data
It does NOT:
- Decide business rules
- Validate logic
exports.findByEmail = async (email) => {
return db("users").where({ email }).first();
};
Repository is an abstraction over the database. If you switch from PostgreSQL to MongoDB, only the repository layer should change. Think of the repository as the translator between your app and the database.
Database – The Storage Engine
This is your:
- PostgreSQL
- MySQL
- MongoDB
- Supabase
Its job is simple:
- Store data
- Retrieve data
- Maintain integrity
It does not care about HTTP, business logic, or application rules; it only stores information.
Full Request Flow
- 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.
Database → Repository → Service → Controller → Client
Clean. Predictable. Scalable.
🧠 Simple Mental Model
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 a 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.