Node.js v7 – URLs, deprecation warnings and a better developer experience

Blogpost nodejs

Node.js v7 is out! It includes goodies like 98% coverage of EcmaScript 6 language features (exciting times!), improved performance and stability and shipped with a new experimental URL parser. The crazy thing about this release is that it shipped only six months after the previous release v6. Node.js now follows a strict defined release schedule and that’s a really good thing.

The Node.js release schedule

After being stuck on really old versions (0.10 and 0.12) for ages Node.js started moving and changed its release strategy to a long term support (LTS) release schedule. This is great for stability and helps the project to move further in a planned and predictable way. It defines planned releases every six month.

Overview of the Node.js release schedule showing releases every 6 month

Even-numbered releases will happen in April and odd-numbered releases will get out in October. There is one speciality about even-numbered releases. These will enter long term support (LTS) when the next odd-numbered release is published.

Long term support in this case means that a release will be actively maintained for the next 18 months and will enter maintenance mode for another 12 months afterwards. When you’re relying on Node.js in production, even-numbered LTS releases are most likely the ones you should use to receive maintenance as long as possible.

If you want to dig deeper into the release process and schedules, you can check the actual documentation about this topic or watch a great talk by Myles Borins on Node.js release processes and how these work.

Node.js v7 includes several breaking changes. Let’s have a look at things I noticed when reading the Node.js changelog and the changes I found most interesting.

Remove functionality in steps via deprecation

To make upgrades to new versions easier, Node.js follows a very developer-friendly deprecation strategy. Features and functionality that are planned to be removed will emit deprecating messages to warn developers about what will change or be removed in future releases. I really like this approach as it notifies developers upfront and prevents us hunting unexpected exceptions when switching to a new version.

Looking into the source the deprecation logging is done via process.emitWarning.

1
2
3
4
5
6
7
8
9
10
11
// lib/buffer.js
process.emitWarning(
  'Using Buffer without `new` will soon stop working. ' +
  'Use `new Buffer()`, or preferably ' +
  '`Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.',
  'DeprecationWarning'
);


// output
(node:99477) DeprecationWarning: Using Buffer without `new` will soon stop working. Use `new Buffer()`, or preferably `Buffer.from()`, `Buffer.allocUnsafe()` or `Buffer.alloc()` instead.

process.emitWarning is something I haven’t used before and it turns out it’s fairly new and landed in Node.js v6. James M Snell wrote an excellent summary of what this is about.

In short – this new “process warning” API landed in Node.js v6 and it makes it easy to emit and log warnings and messages. The nice thing about this API is, that for the case of deprecation messages additional flags are supported. These flags can be used to disable, adjust or change the logging functionality.

There is for example the --throw-deprecation flag, which is a perfect tool for the Node.js version upgrade workflow. Using the flag changes the behavior from logging warnings to throwing exceptions. This is a nifty feature which helps to identify deprecated functions, because thrown exceptions might be easier to spot than logged messages.

In case you’re interested and want to read some Node.js internal code, you can find the exact implementation of the “process warning” API in the internal Node.js file warning.js.

Smart usage of getters and setters

For deprecating object properties Node.js uses a smart way of JavaScript getters and setters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// lib/cluster.js
Object.defineProperty(this, 'suicide', {
  get: internalUtil.deprecate(
    () => { return this.exitedAfterDisconnect; },
    'worker.suicide is deprecated. ' +
      'Please use worker.exitedAfterDisconnect.'
  ),
  set: internalUtil.deprecate(
    (val) => { this.exitedAfterDisconnect = val; },
    'worker.suicide is deprecated. ' +
      'Please use worker.exitedAfterDisconnect.'
  ),
  enumerable: true
});

This way the provided functionality can still be the same, but as seen in the example any property can be “decorated” with additional functionality. The called util object in this case uses also process.emitWarning internally.

That’s a perfect way to ship the same functionality to users and be able to inform them about upcoming changes.

Deprecations and removals

For the sake of completion I just want to list the deprecations and removals here. You should definitely have a look at the changelog to get a better understanding of what these mean for you and what you should stop using.

Deprecations:

  • Constructing Buffers via Buffer() without using new
  • worker.suicide
  • Calling async fs methods without a callback
  • v8BreakIterator()
  • the punycode module
  • REPL#convertToContext()
  • os.tmpDir()

Removals:

  • fs._stringToFlags()
  • http.createClient()
  • module.requireRepl()
  • process.EventEmitter
  • readline functions (codePointAt(), getStringWidth(), isFullWidthCodePoint(), stripVTControlCharacters())

But let’s look at the two changes that caught my eye especially.

Experimental URL parsing is now based on WHATWG standard

A tiny addition sneaked into the v7 release. There is now a new experimental property on the url module.

1
2
3
const URL = require(url).URL;
const webUrl = new URL(https://example.com’);
console.log(webUrl.host);

This new URL property is a new URL parser based on the WHATWG spec and this is really exciting! Why?

Because this means, that maybe someday parsing URLs in Node.js and the browser landscape relies on the same spec. This would make sharing code dealing with URLs between environments way easier. Today the url module is a complete re-implementation of the same functionality. It only has a slightly different API and a few more bugs as James M. Snell points out.

This new URL parser is highly experimental and not even included in the url module documentation. The interesting part about this addition is the time it needed to go into Node.js. The proposal was opened in June in the node-eps project (the GitHub repo dealing with platform additions and major changes) and it landed four month later. That is fairly quick I would say.

Reading the proposal and the following discussions between several Node.js contributors and specification writers is really interesting. It covers main questions like: if this addition makes sense and if it should be implemented as a Node.js global variable. Definitely worth reading to learn more about Node.js.

Promise rejection handling will change in future

The whole JavaScript ecosystem moves to use promises. This is a really good thing as it makes code easier to read, improves error handling and helps to get rid of nasty “callback trees”. The tricky part with promises is that these can “eat” errors quite easily by forgetting to set a proper catch handler.

Whenever using promises the first thing should be to add a proper catch handler.

In Node.js v6 we could easily run into the situation where a promise has been rejected and we won’t notice.

1
2
3
4
// promise-rejection.js
const rejectedPromise = new Promise((resolve, reject) => {
  reject('Something bad happened');
});

In v6 this process would simply terminate and that’s it. Node.js v7 on the other hand now terminates after showing a UnhandledPromiseRejectionWarning and a DeprecationWarning.

1
2
3
4
5
$ node promise-rejection.js


(node:65526) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Something bad happened
(node:65526) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

To avoid these messages either use a proper catch or attach an event handler for the unhandledRejection event on the process object.

Interesting thing about this deprecation warning is that it points out that in future versions unhandled promise rejection will also lead to process termination. That’s a really important piece of information. So, never forget to catch the errors!

This logging aligns with Chrome, which shows a console message for not handled promise rejections. I tested also Safari and Firefox which don’t inform about this case.

A solid step in the right direction

After analyzing the changes in detail I’m really impressed how professional and straight forward Node.js releases go these days. I’m a big fan of the deprecation communication strategy and I think there are more great things to come pushing Node.js and JavaScript further in the future.

Additional resources

Blog posts in your inbox

Subscribe to receive most important updates. We send emails once a month.