Routing in Express: Building Scalable API Endpoints
Master Express routing to map URLs to backend logic. Learn to create GET routes, handle dynamic parameters, and organize your API for long-term scalability.

Previously in this course, we covered the HTTP request-response cycle and performed the initial Express.js setup to get a server listening on a port. Now that we have a running server, we need to teach it how to handle specific requests.
Routing in Express determines how an application responds to a client request at a particular endpoint (a URI and a specific HTTP request method). If you think of your server as a switchboard, routing is the operator connecting the caller to the right department.
Defining Basic GET Routes
A route in Express is defined using methods on the app object that correspond to HTTP methods. For retrieving data, we use app.get().
The app.get() method takes two primary arguments:
- Path: The URL string (e.g.,
/users). - Callback: A function executed when the request matches the path. This function receives the
req(request) andres(response) objects.
JAVASCRIPTconst express = require(CE9178">'express'); const app = express(); app.get(CE9178">'/', (req, res) => { res.send(CE9178">'Welcome to the Task API'); }); app.get(CE9178">'/tasks', (req, res) => { res.json({ message: CE9178">'List of all tasks' }); });
Dynamic Route Parameters
Real-world APIs rarely serve static data. You need to fetch specific resources, such as a task with a unique ID. We handle this using route parameters, which are named URL segments used to capture values at specific positions in the URL.
You define them by prefixing the segment with a colon (:). These values are then accessible in the req.params object.
JAVASCRIPT// Accessing a specific task by ID app.get(CE9178">'/tasks/:taskId', (req, res) => { const { taskId } = req.params; res.json({ message: CE9178">`Fetching details for task ID: ${taskId}` }); });
If a user visits /tasks/123, req.params.taskId will equal "123".
Organizing Routes for Scalability
As your project grows, putting every route in your main index.js file will quickly become unmanageable. To keep your code clean, we use the express.Router class to create modular, mountable route handlers.
Think of the Router as a "mini-app" that handles its own set of routes.
1. Create a route file (routes/taskRoutes.js)
JAVASCRIPTconst express = require(CE9178">'express'); const router = express.Router(); router.get(CE9178">'/', (req, res) => res.send(CE9178">'Task List')); router.get(CE9178">'/:id', (req, res) => res.send(CE9178">`Task ${req.params.id}`)); module.exports = router;
2. Mount the router in index.js
JAVASCRIPTconst taskRoutes = require(CE9178">'./routes/taskRoutes'); // All routes in taskRoutes will be prefixed with /api/tasks app.use(CE9178">'/api/tasks', taskRoutes);
This approach allows you to group related endpoints (like users, authentication, or tasks) into separate files, making your API architecture far easier to navigate.
Hands-on Exercise
- Inside your project folder, create a directory named
routes. - Inside
routes, create a file calleduserRoutes.js. - Define a GET route at
/that returns a JSON object:{ users: [] }. - Define a GET route at
/:usernamethat returns a message:Hello, [username]!. - Import this router into your main
index.jsand mount it at/users. - Start your server and test these endpoints using your browser or a tool like Postman.
Common Pitfalls
- Order of Routes: Express matches routes in the order they are defined. If you define a generic route like
/tasks/:idbefore a specific route like/tasks/active, the generic route might catch the request first. Always place more specific routes above dynamic or wildcard routes. - Missing
next(): If your callback function doesn't send a response (e.g.,res.sendorres.json) and doesn't callnext(), the client will hang indefinitely. - Hardcoding Paths: Avoid repeating base paths. Use
app.use()to prefix your routes (like/api/v1) rather than typing the prefix into every single route definition.
FAQ
Q: Can I have multiple parameters in one path?
A: Yes. You can define paths like /users/:userId/tasks/:taskId. Both userId and taskId will be available in req.params.
Q: What is the difference between req.params and req.query?
A: req.params are for identifying a specific resource (e.g., /tasks/123). req.query is for filtering or sorting a collection (e.g., /tasks?status=completed). We will cover query parameters in a later lesson.
Recap
We've successfully moved from a single-file server to a modular, scalable architecture. You now know how to define GET routes, capture dynamic IDs using route parameters, and use express.Router to keep your project organized. Mastering this structure is essential for building a clean, professional API.
Up next: Handling HTTP Methods — we will expand our server to handle POST, PUT, and DELETE operations to make our API truly interactive.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

Custom Email & File Storage System on Cloudflare (Google Workspace Alternative)
Your own private email + file storage suite on your domain — unlimited mailboxes, no per-seat fees. A self-owned Google Workspace alternative for a flat ~$5/month.


