As of February 11, 2020, the HTTP request tool has been deprecated on NPM, and is basically not searchable on NPM unless you access the address directly. See Issue #3142 Request’s Past, Present and Future, after which Node-fetch, Axios might be a good choice, See issues#3143 Alternative libraries to request for a comparison of some common node.js HTTP clients.

Today I’d like to recommend a new Node.js HTTP Client, Undici, which is faster than the built-in HTTP module, with benchmark data below. The Undici team is dedicated to developing fast, reliable, and compliant HTTP clients for Node.js, and the project is located under the Node.js Github organization. Several of the contributors are also contributors to the Node.js project, so it’s worth keeping an eye on.

background

“A lot of people still ask us why we’re building an alternative to the Node.js core HTTP stack — even though it works fine for them. The reality is that the node.js core HTTP stack has fundamental design issues that cannot be overcome without breaking the API. We can’t fix some bugs or performance bottlenecks — either on the client or server implementations — without breaking the majority of our users, because they’re so closely linked.” Reference nodejs.medium.com/introducing…

The benchmark

The following is a benchmark test done on Node.js 16, and by comparing it to the slowest data, the difference is quite large.Data Source:Undici.nodejs.org/#/?id=bench…

Basic use of undici

This is an NPM module, first you need to install and reference it, in order to easily use the Top Level Await feature, the ES Modules module specification is used below.

npm i undici -S
import undici from 'undici';
Copy the code

Start a Server

To start, let’s start a Server and then use undici’s HTTP client to ask the local Server to do some testing.

// server.mjs
import http from 'http';
const server = http.createServer((req, res) = > {
  res.end(`Ok! `);   
});
server.listen(3000.() = > {
	console.log('server listening at 3000 port');
});

// The terminal starts
node server.mjs
Copy the code

Use the Request request interface

It returns results that support asynchronous iterators, you can use for await… Of traverses the body data returned.

For those unfamiliar with asynchronous iterators, explore the use of asynchronous iterators in Node.js.

const {
  statusCode,
  headers,
  trailers,
  body
} = await undici.request('http://localhost:3000/api', {
  method: 'GET'
});
console.log('response received', statusCode)
console.log('headers', headers)
for await (const data of body) {
  console.log('data', data.toString());
}
console.log('trailers', trailers)
Copy the code

Create a client instance request interface

Undici provides the Client class, which can be passed in urls or URL objects. It contains only the protocol, hostname, and port, and is used to pre-create a basic generic Client request instance.

We can also listen for a’ data’ event on the return result and get the data in response, just as we used to listen for a’ data’ event when reading data from a file as a stream, but now reading data as a stream also supports asynchronous iteration. See exploring the use of asynchronous iterators in Node.js.

const client = new undici.Client('http://localhost:3000');
const { body } = await client.request({
  path: '/api'.method: 'GET'}); body.setEncoding('utf-8');
body.on('data'.console.log);
body.on('end'.() = > console.log('end'));
Copy the code

Use the stream() method as the interface proxy

Unlike the Request method, the client.stream method expects the Factory to return a Writable that will write a Response. Performance is improved by avoiding creating an intermediate Readable when the user expects the Response Body to be passed directly to Writable.

const factory = ({ opaque: res }) = > res;
const client = new undici.Client('http://localhost:3000');
http.createServer((req, res) = > {
  client.stream({
    path: '/'.method: 'GET'.opaque: res
  }, factory, (err) = > {
    if (err) {
      console.error('failure', err)
    } else {
      console.log('success')}}); }).listen(3010);
Copy the code

Use stream to read an image from the network and write it locally

If you are not familiar with the undici. Stream example, see the following scenario. First, read the image from the network, return the value itself is a readable stream object, and now specify a writable stream from opaque.

const factory = ({ opaque: res }) = > res;
const url = 'https://images.pexels.com/photos/3599228/pexels-photo-3599228.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500';
undici.stream(url, {
  opaque: fs.createWriteStream('./pexels-photo-3599228.jpeg')
}, factory, (err) = > {
  if (err) {
    console.error('failure', err)
  } else {
    console.log('success')}});Copy the code

It was done before.

import request from 'request';
import fs from 'fs';
const readable = request('https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1586785739&di=ba1230a76a 1ebd25200448d5cf02ec40&src=http://bbs.jooyoo.net/attachment/Mon_0905/24_65548_2835f8eaa933ff6.jpg');
const writeable = fs.createWriteStream('./pexels-photo-3599228.jpeg');
readable.pipe(writeable)
readable.on('error'.function(err) {
  writeable.close();
});
Copy the code

Abort a request

You can abort a client request using the controller object AbortController or EventEmitter. To use AbortController in versions prior to V15.x, you need to install and import this module. The latest v15.x release is not required and is supported by default.

// npm i abort-controller
import AbortController from "abort-controller"

const abortController = new AbortController();
client.request({
  path: '/api'.method: 'GET'.signal: abortController.signal,
}, function (err, data) {
  console.log(err.name) // RequestAbortedError
  client.close();
});
abortController.abort();
Copy the code

In addition, any instance of EventEmitter that emits an ‘abort’ event can be used as an abort controller.

import { EventEmitter } from 'events';
const ee = new EventEmitter();
client.request({
  path: '/api'.method: 'GET'.signal: ee,
}, function (err, data) {
  console.log(err.name) // RequestAbortedError
  client.close();
});
ee.emit('abort');
Copy the code

undici-fetch

WHATWG FETCH is a WHATWG FETCH implementation built on top of Undici. Just like you used Node-fetch before, you can choose to use Undici-Fetch to simply handle some requests.

// npm i undici-fetch
import fetch from 'undici-fetch';
const res = await fetch('http://localhost:3000');
try {
  const json = await res.json();
  console.log(json);
} catch (err) {
  console.log(err);
}
Copy the code

Reference

  • Explore the use of asynchronous iterators in Node.js
  • Github.com/Ethan-Arrow…
  • Github.com/nodejs/undi…

Originally published on Nodejs Technology Stack.