Episode 3 — NodeJS MongoDB Backend Architecture / 3.2 — Creating Server
3.2 — Interview Questions: Creating a Server
Common interview questions about Node.js HTTP servers, the request-response cycle, routing, and status codes.
< Exercise Questions | Quick Revision >
Beginner Level
Q1: What is the http module in Node.js, and how do you create a basic HTTP server?
Why interviewers ask this: They want to confirm you understand that Node.js has built-in server capabilities and that you can write one without any framework. This tests your grasp of the foundation that Express and other frameworks build upon.
Model answer: The http module is a built-in Node.js module that provides functionality to create HTTP servers and make HTTP requests. No installation is needed. You create a server by calling http.createServer() and passing it a callback function that receives two arguments: req (the incoming request object, an instance of http.IncomingMessage) and res (the response object, an instance of http.ServerResponse). You then call server.listen(port) to start listening for incoming connections. The callback fires once for every incoming HTTP request, regardless of the URL or method. This is the lowest-level way to build a web server in Node.js, and understanding it helps you appreciate what Express does under the hood.
Q2: What are the req and res objects in a Node.js HTTP server? What are their most important properties and methods?
Why interviewers ask this: The request and response objects are the core of every HTTP interaction. Interviewers want to see that you know what information is available on each and how to use them correctly.
Model answer: The req object (http.IncomingMessage) represents the incoming request from the client. Its most important properties are: req.method (the HTTP verb: GET, POST, etc.), req.url (the full URL path including query string), req.headers (an object of all request headers, with names lowercased), and req.httpVersion. For POST/PUT requests, the body arrives as a stream and must be collected using req.on('data') and req.on('end') events.
The res object (http.ServerResponse) provides tools to build and send the response. Key methods are: res.writeHead(statusCode, headers) to set the status code and headers in one call, res.setHeader(name, value) to set headers individually, res.write(data) to write chunks to the response body, and res.end(data) to finish the response. You must always call res.end() or the client will hang indefinitely waiting for a response that never completes.
Q3: What is a Content-Type header and why is it important?
Why interviewers ask this: Mismatched content types are a common source of bugs, especially when building APIs. This question reveals whether you understand how clients and servers communicate about data format.
Model answer: The Content-Type header is a MIME type that tells the receiver how to interpret the body of an HTTP message. On a response, it tells the browser or client what kind of data the server is sending. For example, text/html tells the browser to render HTML, application/json tells it to parse JSON, and text/plain tells it to display raw text.
If you send JSON data but set Content-Type: text/html, the browser will try to render the JSON as HTML, producing garbled output. If you omit the header entirely, the browser falls back to MIME sniffing, which is unreliable and can introduce security vulnerabilities.
On a request, the Content-Type header tells the server what format the request body is in. A value of application/json means the body is JSON; application/x-www-form-urlencoded means it is form data. The server uses this to decide how to parse the incoming body.
Q4: What is the difference between res.writeHead() and res.setHeader()?
Why interviewers ask this: This tests attention to the API surface of Node.js and understanding of when headers are sent over the wire.
Model answer: res.writeHead(statusCode, headers) sets the status code and multiple headers in a single call. It can only be called once and is typically used when you know all your headers upfront. res.setHeader(name, value) sets a single header at a time and can be called multiple times. It does not set the status code; you use res.statusCode = 200 separately.
Headers are not sent to the client until res.write() or res.end() is called for the first time (this is called the "implicit header" flush). If you call res.writeHead(), it takes precedence over any headers set with res.setHeader(). A practical difference is that res.setHeader() is better when middleware or conditional logic needs to add headers incrementally before the response is sent.
Intermediate Level
Q5: Explain how routing works in a raw Node.js HTTP server without Express. What are the limitations?
Why interviewers ask this: This separates candidates who have only used Express from those who understand what happens underneath. It also tests your ability to identify trade-offs.
Model answer: In a raw Node.js HTTP server, routing is done manually by inspecting req.url and req.method inside the request handler callback. The simplest approach uses if/else chains or a switch statement to compare these values against known paths.
For example, you check if (req.url === '/api/users' && req.method === 'GET') to handle a GET request to that path. For dynamic segments like /api/users/42, you use req.url.split('/') or regex to extract the parameter. For query strings, you use new URL(req.url, base) to parse them.
The limitations are significant: there is no built-in support for path parameters (:id), no middleware pipeline for shared logic like logging or authentication, no automatic body parsing (you must manually collect stream chunks), no built-in static file serving, and the if/else chain becomes unmanageable as routes grow. These are exactly the problems that Express solves. However, understanding manual routing gives you deep knowledge of what Express does internally, such as converting path patterns to regular expressions and matching them in order.
Q6: What are the five categories of HTTP status codes? Give one concrete example of when you would use a code from each category.
Why interviewers ask this: Status codes are a contract between server and client. Using them incorrectly breaks API consumers, frontend logic, and monitoring tools. This is a fundamental web knowledge question.
Model answer: The five categories are:
1xx Informational -- The server acknowledges the request and processing continues. Example: 101 Switching Protocols when upgrading an HTTP connection to a WebSocket connection.
2xx Success -- The request succeeded. Example: 201 Created after a POST request successfully creates a new user in the database. You should also include a Location header pointing to the new resource.
3xx Redirection -- The client needs to take additional action. Example: 301 Moved Permanently when an old URL has been permanently replaced, such as redirecting http:// to https://. The Location header tells the client where to go.
4xx Client Error -- The client sent a bad request. Example: 404 Not Found when a user requests GET /api/users/999 but user 999 does not exist in the database.
5xx Server Error -- The server failed on a valid request. Example: 500 Internal Server Error when an unhandled exception occurs, such as a database connection dropping mid-query. The key rule is never exposing internal error details to the client; log the full stack trace server-side and return a generic message.
Q7: What is the difference between 401 Unauthorized and 403 Forbidden? When would you use each?
Why interviewers ask this: This is one of the most commonly confused pairs of status codes. Getting it right shows precise understanding of authentication versus authorization.
Model answer: Despite its name, 401 is about authentication, not authorization. It means "I do not know who you are." You return 401 when no credentials are provided, when a token is expired, or when credentials are invalid. The response should include a WWW-Authenticate header indicating the expected authentication scheme (e.g., Bearer).
403 means "I know who you are, but you are not allowed to do this." The client has successfully authenticated, but their identity does not have permission for the requested action. For example, a regular user trying to access an admin-only endpoint.
Concrete scenario: A request to DELETE /api/users/5 arrives. If there is no Authorization header at all, return 401. If the header contains a valid token for a user whose role is "viewer" (not "admin"), return 403. If the token is expired or malformed, return 401.
Q8: How does Node.js handle incoming request bodies? Why does the body arrive in chunks?
Why interviewers ask this: This tests understanding of Node.js streams and the asynchronous I/O model, which are core to how Node.js works.
Model answer: In Node.js, the req object is a readable stream. The body does not arrive all at once; it arrives in chunks because HTTP data is transmitted over TCP, which breaks data into packets. Node.js exposes this through the stream interface: you listen for 'data' events to collect each chunk, and the 'end' event to know when the entire body has been received.
let body = '';
req.on('data', (chunk) => { body += chunk.toString(); });
req.on('end', () => {
const parsed = JSON.parse(body);
// now use parsed data
});
This streaming approach is efficient because it does not buffer the entire body in memory before your code runs. For large uploads, you can process chunks as they arrive instead of waiting for the full payload. This is why Express's express.json() middleware exists; it abstracts this manual chunk collection into a single middleware call.
A common mistake is trying to read req.body directly without middleware in a raw HTTP server. Unlike Express (which attaches parsed body via middleware), the raw http module has no req.body property.
Advanced Level
Q9: If you had to build a mini routing framework on top of the http module, how would you design it?
Why interviewers ask this: This tests system design thinking and deep understanding of how frameworks like Express work internally. It shows whether you can think beyond using tools to building them.
Model answer: I would create a Router class that maintains an array of route definitions. Each route has a method, a path pattern, and a handler function.
For path matching, I would convert patterns like /users/:id into regular expressions by replacing :paramName segments with capture groups like ([^/]+). Each route object stores the parameter names alongside its regex.
The main handle(req, res) method iterates through registered routes in order. For each route, it checks if the HTTP method matches and if the URL pathname matches the regex. On a match, it extracts named parameters from the regex capture groups, attaches them to req.params, parses the query string into req.query using the URL constructor, and calls the handler. If no route matches, it sends a 404 response.
I would add shorthand methods like router.get(path, handler) and router.post(path, handler) that delegate to a generic addRoute(method, path, handler) method.
The key lessons this teaches are: route matching is fundamentally regex-based, routes are checked in registration order (first match wins), and dynamic parameters are extracted via capture groups. Express works exactly this way, with additional features like middleware chains, error handling middleware, and sub-routers.
Q10: A production Node.js server starts returning 503 errors intermittently. Walk through your debugging approach.
Why interviewers ask this: This tests real-world operational thinking, not just code knowledge. Interviewers want to see a structured approach to diagnosing production issues.
Model answer: I would follow a systematic approach:
Step 1: Check logs. Look at application logs for error messages or stack traces around the time of the 503 responses. A 503 typically means the server is overloaded or a dependency is down.
Step 2: Check server health metrics. Look at CPU usage, memory consumption, and event loop lag. If the event loop is blocked, the server cannot process requests. High memory might indicate a leak causing garbage collection pauses.
Step 3: Check external dependencies. Since 503 often means "service unavailable," verify that the database, cache (Redis), and any external APIs the server depends on are healthy. If the database connection pool is exhausted, the server cannot fulfill requests.
Step 4: Check for resource exhaustion. Verify the server has not hit file descriptor limits, connection limits, or port exhaustion. Check if the EADDRINUSE or EMFILE errors appear in logs.
Step 5: Check load and traffic patterns. Determine if the 503 errors correlate with traffic spikes. If so, the server may need horizontal scaling, rate limiting, or a load balancer.
Step 6: Check recent deployments. Determine if a recent code change introduced a regression, such as a synchronous operation blocking the event loop or a missing await causing unhandled promise rejections.
The key point is that 503 is the correct status code when the server genuinely cannot handle requests temporarily, and it should include a Retry-After header to help clients back off gracefully.
Q11: Explain the security considerations when building an HTTP server with the raw http module. What protections does Express add that you would have to build yourself?
Why interviewers ask this: This tests security awareness and the practical understanding of why frameworks exist beyond convenience.
Model answer: With the raw http module, several security concerns require manual handling:
Directory traversal: When serving static files, an attacker can request GET /../../../etc/passwd to read files outside your public directory. You must validate that the resolved file path stays within the intended directory using path.resolve() and checking it starts with your public root.
Request body limits: The raw http module does not limit request body size. An attacker can send a multi-gigabyte body to exhaust server memory. You must manually track incoming bytes and abort the connection if a threshold is exceeded.
Header injection: If you interpolate user input into response headers without sanitization, attackers can inject additional headers or even response splitting attacks.
Error information leakage: Without a centralized error handler, it is easy to accidentally send stack traces or internal error details to clients in 500 responses. You must wrap all handlers in try/catch and return generic messages.
CORS: Without explicit Access-Control-Allow-Origin headers, browsers will block cross-origin requests to your API.
Express adds: express.json({ limit: '1mb' }) for body size limits, helmet middleware for security headers, cors middleware for CORS configuration, built-in error handling middleware, and express.static() with directory traversal protection. When building on the raw http module, every one of these protections must be implemented manually.
Quick-Fire Questions
These are short-answer questions commonly asked in rapid-fire interview rounds.
| # | Question | Expected Answer |
|---|---|---|
| 1 | What Node.js module creates an HTTP server? | http (built-in, no install needed) |
| 2 | What two arguments does the createServer callback receive? | req (IncomingMessage) and res (ServerResponse) |
| 3 | What method starts the server listening? | server.listen(port, callback) |
| 4 | What property gives you the HTTP method? | req.method |
| 5 | What property gives you the URL path? | req.url |
| 6 | How do you send a JSON response? | res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); |
| 7 | What happens if you forget res.end()? | The client hangs indefinitely waiting for the response. |
| 8 | What status code for a created resource? | 201 Created |
| 9 | What status code for "not authenticated"? | 401 Unauthorized |
| 10 | What status code for "authenticated but not permitted"? | 403 Forbidden |
| 11 | What status code for a missing resource? | 404 Not Found |
| 12 | What status code for a server bug? | 500 Internal Server Error |
| 13 | What header is required for redirects? | Location |
| 14 | What does 204 No Content mean? | Success, but the response has no body (common after DELETE). |
| 15 | What is localhost? | A hostname that resolves to 127.0.0.1 -- your own machine. |
| 16 | What tool auto-restarts the server on file changes? | nodemon (or node --watch in Node 18.11+) |
| 17 | Why is port 3000 used for Node.js? | Convention only. Any port above 1023 works. |
| 18 | What does Content-Type: application/json tell the client? | The response body is JSON data. |
| 19 | How do you read a POST body in raw Node.js? | Collect chunks with req.on('data'), parse on req.on('end'). |
| 20 | What npm script runs nodemon by convention? | npm run dev |
Navigation: < Exercise Questions | Quick Revision >