This article is published in English.
Seven Beginner Node.js and Express Coding Tasks, Solved and Explained
Work through seven typical junior Node.js interview tasks, from Express routes and middleware to error handling, string reversal and duplicates, and learn to explain each one.
Junior Node.js and Express interviews rarely hinge on clever algorithms. You usually get short tasks, such as building a route, writing a middleware or cleaning up an array, while the interviewer watches how you reason and whether you can explain why your code works. The seven tasks below cover the most common ground, each with a working solution, a walkthrough of the key lines and the follow-up points that reveal real understanding.
Task 1: Build a basic GET route
A classic warm-up: "Expose a GET endpoint at / that responds with Hello World." The solution creates an Express app, registers a handler for the root path and starts listening on a port.
import express from "express";
const app = express();
app.get("/", (req, res) => {
res.status(200).send("Hello World");
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
Because it uses import, the project needs "type": "module" in package.json or a .mjs extension.
Reading the route line by line
The call to app.get() registers a handler that only reacts to GET requests:
app.get("/", (req, res) => {
The first argument, /, is the path. The callback receives req, describing the incoming request, and res, used to build the reply.
The reply itself is produced here:
res.status(200).send("Hello World");
status(200) sets the status code (200 is the default, but being explicit reads well), and send() writes the body and ends the response.
Finally, the server is started:
app.listen(3000, () => {
This binds the app to port 3000; the callback fires once the server accepts connections. Real services usually read the port from process.env.PORT.
How to say it
"
app.get()registers a route for GET requests. When a client calls/, Express runs the callback, which responds with status 200 and Hello World."
Task 2: Write a request-logging middleware
Next prompt: "Create middleware that logs every incoming request." A middleware is a function with the signature (req, res, next). This one prints the method and URL, then hands control onward; app.use() applies it to every route registered after it.
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
};
app.use(logger);
Each request passes through the chain in order before a route handler responds:
Client
↓
Request
↓
Logger Middleware
↓
Route Handler
↓
Response
The single most important line is the last one inside the function:
next();
next() tells Express this step is done and the next middleware or route should run. Forget it, and the request hangs until the client times out, a frequent follow-up question. The alternative is to end the request by sending a response, as an auth middleware does when it rejects a missing token.
How to say it
"Middleware receives the request, the response and
next. It can inspect or modify the request, then either responds or callsnext()to pass control on."
Task 3: Handle asynchronous work in a route
Real endpoints wait on databases and other APIs, so expect "How do you handle an async operation in Express?" A solid answer is an async handler with try/catch:
app.get("/users", async (req, res, next) => {
try {
const users = await getUsers();
res.status(200).json(users);
} catch (error) {
next(error);
}
});
The key line is the await:
const users = await getUsers();
It pauses this handler until getUsers() resolves, without blocking other requests. If the promise rejects, the catch block runs:
catch (error) {
next(error);
}
next(error) hands the failure to the error-handling middleware from Task 5. Worth adding: Express 4 does not catch rejected promises from async handlers, so the try/catch is essential there, while Express 5 forwards them automatically. For more depth, see building production-grade error handling in Node.js apps.
Task 4: Sketch CRUD endpoints
CRUD stands for Create, Read, Update and Delete, the four basic data operations, each mapped to an HTTP method. Stubs for a users resource:
app.post("/users", (req, res) => {
res.status(201).send("User created");
});
app.get("/users", (req, res) => {
res.status(200).send("Users");
});
app.put("/users/:id", (req, res) => {
res.status(200).send("User updated");
});
app.delete("/users/:id", (req, res) => {
res.status(200).send("User deleted");
});
Creation returns 201 Created, and single-user routes take an :id parameter read from req.params.id. Rather than memorizing lines, know what each method means:
POST → Create
GET → Read
PUT → Update
DELETE → Delete
Be ready for PUT versus PATCH: PUT conventionally replaces the whole resource, PATCH applies a partial change.
How to say it
"CRUD covers the four basic data operations. In REST, POST creates, GET reads, PUT or PATCH updates, and DELETE removes."
Task 5: Centralize error handling
Error handling often follows Task 3. Start with a route that fails and forwards the error:
app.get("/", (req, res, next) => {
try {
throw new Error("Something went wrong");
} catch (error) {
next(error);
}
});
Then add an error handler that logs and returns a generic 500:
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).json({
message: "Internal Server Error"
});
});
What makes this function special is its signature:
(err, req, res, next)
Express recognizes error handlers by their four declared parameters, in the order err, req, res, next; they run only when an error was passed along. Register the handler after your routes, and avoid sending err.message to clients in production. The flow:
Route
↓
Error occurs
↓
catch(error)
↓
next(error)
↓
Error middleware
↓
500 Response
Is next() required inside the error handler?
Not if the handler sends the response itself:
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).send("Internal Server Error");
});
next must still be declared, because with three parameters Express treats the function as ordinary middleware.
Task 6: Reverse a string
Some questions are plain JavaScript: "Reverse the string hello." The idiomatic one-liner chains three methods:
const str = "hello";
const reversed = str.split("").reverse().join("");
console.log(reversed);
It prints:
olleh
What each step does
Splitting yields characters:
str.split("")
The result is an array:
["h", "e", "l", "l", "o"]
Then the order is flipped in place:
.reverse()
That gives:
["o", "l", "l", "e", "h"]
Finally the characters are joined with no separator:
.join("")
That produces the reversed string:
"olleh"
A common twist: "Now do it without reverse()." Loop from the last index down to zero:
const str = "hello";
let reversed = "";
for (let i = str.length - 1; i >= 0; i--) {
reversed += str[i];
}
console.log(reversed);
Both are linear. Worth mentioning: split("") and indexing work on UTF-16 code units, so emoji get broken apart; [...str] splits by code point instead.
Task 7: Find the duplicates in an array
Another favorite: "Return values that occur more than once." Given:
const numbers = [1, 2, 3, 2, 4, 3, 5];
the expected result is:
[2, 3]
Use two Set objects, one for values seen and one for confirmed duplicates:
const numbers = [1, 2, 3, 2, 4, 3, 5];
const seen = new Set();
const duplicates = new Set();
for (const number of numbers) {
if (seen.has(number)) {
duplicates.add(number);
} else {
seen.add(number);
}
}
console.log([...duplicates]);
Running it logs:
[2, 3]
Why two Sets work
seen records every value passed. A first occurrence is added there:
seen.add(number);
A repeat makes this check succeed:
if (seen.has(number))
The value then goes into duplicates, and since a Set ignores repeats, it is reported once. Lookups are near constant time, so the solution is linear rather than the quadratic nested loop. To count occurrences, use a Map.
Talk through your approach before you type
The habit that matters most is explaining your plan before writing code, because interviewers grade reasoning as much as results. For the duplicates task, open with something like:
"The plan is a Set of values already seen. While looping, any value already in it goes into a second Set of duplicates, which keeps each one unique."
Then code. This proves understanding over memorization and lets the interviewer redirect you early.
Preparation checklist
Before a junior Node.js or Express interview, make sure you can comfortably:
- Define routes and explain the roles of
reqandres - Use
app.use()and write your own middleware - Explain what
next()does and what happens when it is missing - Handle async work with
async/await,try/catchandnext(error) - Write a four-parameter error handler and register it after the routes
- Map CRUD operations to HTTP methods and suitable status codes
- Manipulate strings and arrays with core JavaScript methods
- Choose between
SetandMapfor lookups and counting - Narrate your approach while you code
At this level, solid fundamentals, readable code and clear explanations matter far more than advanced algorithms.