Gergely Nemeth Co-Founder of RisingStack, EM at Uber

Two years ago we published our first article on common Node.js Interview Questions and Answers. Since then a lot of things improved in the JavaScript and Node.js ecosystem, so it was time to update it.

Important Disclaimers

It is never a good practice to judge someone just by questions like these, but these can give you an overview of the person's experience in Node.js.

But obviously, these questions do not give you the big picture of someone's mindset and thinking.

I think that a real-life problem can show a lot more of a candidate's knowledge - so we encourage you to do pair programming with the developers you are going to hire.

Finally and most importantly: we are all humans, so make your hiring process as welcoming as possible. These questions are not meant to be used as "Questions & Answers" but just to drive the conversation.

Node.js Interview Questions

What is an error-first callback?

How can you avoid callback hells?

What are Promises?

What tools can be used to assure consistent style? Why is it important?

When should you npm and when yarn?

What's a stub? Name a use case!

What's a test pyramid? Give an example!

What's your favorite HTTP framework and why?

How can you secure your HTTP cookies against XSS attacks?

How can you make sure your dependencies are safe?

The Answers

What is an error-first callback?

Error-first callbacks are used to pass errors and data as well. You have to pass the error as the first parameter, and it has to be checked to see if something went wrong. Additional arguments are used to pass data.

fs.readFile(filePath, function(err, data) { if (err) { // handle the error, the return is important here // so execution stops here return console.log(err) } // use the data object console.log(data) })

How can you avoid callback hells?

There are lots of ways to solve the issue of callback hells:

modularization : break callbacks into independent functions

: break callbacks into independent functions use a control flow library , like async

, like async use generators with Promises

use async/await (note that it is only available in the latest v7 release and not in the LTS version - you can read our experimental async/await how-to here)

What are Promises?

Promises are a concurrency primitive, first described in the 80s. Now they are part of most modern programming languages to make your life easier. Promises can help you better handle async operations.

An example can be the following snippet, which after 100ms prints out the result string to the standard output. Also, note the catch , which can be used for error handling. Promises are chainable.

new Promise((resolve, reject) => { setTimeout(() => { resolve('result') }, 100) }) .then(console.log) .catch(console.error)

When working in a team, consistent style is important, so team members can modify more projects easily, without having to get used to a new style each time.

Also, it can help eliminate programming issues using static analysis.

Tools that can help:

If you’d like to be even more confident, I suggest you to learn and embrace the JavaScript Clean Coding principles as well!

What's a stub? Name a use case!

Stubs are functions/programs that simulate the behaviors of components/modules. Stubs provide canned answers to function calls made during test cases.

An example can be writing a file, without actually doing so.

var fs = require('fs') var writeFileStub = sinon.stub(fs, 'writeFile', function (path, data, cb) { return cb(null) }) expect(writeFileStub).to.be.called writeFileStub.restore()

What's a test pyramid? Give an example!

A test pyramid describes the ratio of how many unit tests, integration tests and end-to-end test you should write.

An example for an HTTP API may look like this:

lots of low-level unit tests for models (dependencies are stubbed ),

), fewer integration tests, where you check how your models interact with each other (dependencies are not stubbed ),

), less end-to-end tests, where you call your actual endpoints (dependencies are not stubbed).

What's your favorite HTTP framework and why?

There is no right answer for this. The goal here is to understand how deeply one knows the framework she/he uses. Tell what are the pros and cons of picking that framework.

When are background/worker processes useful? How can you handle worker tasks?

Worker processes are extremely useful if you'd like to do data processing in the background, like sending out emails or processing images.

There are lots of options for this like RabbitMQ or Kafka.

How can you secure your HTTP cookies against XSS attacks?

XSS occurs when the attacker injects executable JavaScript code into the HTML response.

To mitigate these attacks, you have to set flags on the set-cookie HTTP header:

HttpOnly - this attribute is used to help prevent attacks such as cross-site scripting since it does not allow the cookie to be accessed via JavaScript.

- this attribute is used to help prevent attacks such as cross-site scripting since it does not allow the cookie to be accessed via JavaScript. secure - this attribute tells the browser to only send the cookie if the request is being sent over HTTPS.

So it would look something like this: Set-Cookie: sid=<cookie-value>; HttpOnly . If you are using Express, with express-cookie session, it is working by default.

How can you make sure your dependencies are safe?

When writing Node.js applications, ending up with hundreds or even thousands of dependencies can easily happen.

For example, if you depend on Express, you depend on 27 other modules directly, and of course on those dependencies' as well, so manually checking all of them is not an option!

The only option is to automate the update / security audit of your dependencies. For that there are free and paid options:

Node.js Interview Puzzles

The following part of the article is useful if you’d like to prepare for an interview that involves puzzles, or tricky questions.

What's wrong with the code snippet?

new Promise((resolve, reject) => { throw new Error('error') }).then(console.log)

The Solution:

As there is no catch after the then . This way the error will be a silent one, there will be no indication of an error thrown.

To fix it, you can do the following:

new Promise((resolve, reject) => { throw new Error('error') }).then(console.log).catch(console.error)

If you have to debug a huge codebase, and you don't know which Promise can potentially hide an issue, you can use the unhandledRejection hook. It will print out all unhandled Promise rejections.

process.on('unhandledRejection', (err) => { console.log(err) })

What's wrong with the following code snippet?

function checkApiKey (apiKeyFromDb, apiKeyReceived) { if (apiKeyFromDb === apiKeyReceived) { return true } return false }

The Solution:

When you compare security credentials it is crucial that you don't leak any information, so you have to make sure that you compare them in fixed time. If you fail to do so, your application will be vulnerable to timing attacks.

But why does it work like that?

V8, the JavaScript engine used by Node.js, tries to optimize the code you run from a performance point of view. It starts comparing the strings character by character, and once a mismatch is found, it stops the comparison operation. So the longer the attacker has right from the password, the more time it takes.

To solve this issue, you can use the npm module called cryptiles.

function checkApiKey (apiKeyFromDb, apiKeyReceived) { return cryptiles.fixedTimeComparison(apiKeyFromDb, apiKeyReceived) }

What's the output of following code snippet?

Promise.resolve(1) .then((x) => x + 1) .then((x) => { throw new Error('My Error') }) .catch(() => 1) .then((x) => x + 1) .then((x) => console.log(x)) .catch(console.error)

The Answer:

The short answer is 2 - however with this question I'd recommend asking the candidates to explain what will happen line-by-line to understand how they think. It should be something like this:

A new Promise is created, that will resolve to 1 . The resolved value is incremented with 1 (so it is 2 now), and returned instantly. The resolved value is discarded, and an error is thrown. The error is discarded, and a new value ( 1 ) is returned. The execution did not stop after the catch, but before the exception was handled, it continued, and a new, incremented value ( 2 ) is returned. The value is printed to the standard output. This line won't run, as there was no exception.

A day may work better than questions

Spending at least half a day with your possible next hire is worth more than a thousand of these questions.

Once you do that, you will better understand if the candidate is a good cultural fit for the company and has the right skill set for the job.

Do you miss anything? Let us know!

What was the craziest interview question you had to answer? What's your favorite question / puzzle to ask? Let us know in the comments! :)