Use the Node.js HTTP Module to Make a Request
Tags: node, http, webdev, tutorial
Published: 2020-04-29 16:04:47

_📣 This post originally appeared as [Use the Node.js HTTP Module to Make a Request](https://blog.bearer.sh/node-http-request/) on [The Bearer Blog](https://blog.bearer.sh)._ 
<!--kg-card-begin: markdown-->

The ecosystem around making requests in Node.js applications is huge. With [countless libraries available](https://blog.bearer.sh/top-node-request-libraries/), it can be helpful to understand why they exist. This helps to improve your understanding of Node.js as a whole, and makes choosing an HTTP client easier.

In the [first post in this series](https://blog.bearer.sh/create-node-http-server/), we looked at creating servers using Node's `http` module. In this post, we will explore making a request using the `http.request` method and handling the response. This is the underlying component used by most, if not all, Node.js request libraries.

## http.request basics

The `request` method is part of Node's built-in `http` module. This module handles much of the low-level functionality needed to create servers, receive requests, send responses, and keep connections open. The `request` implementation, like most core implementations, is rather verbose and harder to work with than the abstractions that many libraries implement. It is also event-driven, and relies on [streams](https://nodejs.org/api/stream.html#stream_class_stream_readable) to handle data. This doesn't mean it isn't usable. In fact, many parts of it will look familiar to third-party libraries as they have drawn inspiration from it. Let's start with a basic request.

```javascript
const http = require("http")

http
  .request(
    {
      hostname: "postman-echo.com",
      path: "/status/200"
    },
    res => {
      let data = ""

      res.on("data", d => {
        data += d
      })
      res.on("end", () => {
        console.log(data)
      })
    }
  )
  .end()
```

This code block makes a `GET` request to `http://postman-echo.com/status/200` and logs the response to the console. The `request` method can take a [variety of configuration options](https://nodejs.org/api/http.html#http_http_request_options_callback). In this example, we are passing it the hostname and path. We didn't set a method, because `GET` is the default. The callback takes the response—`res` in the example—which can listen for events that fire during the response.

This example focuses on two key events. The `data` event and the `end` event. Because the response comes as a readable stream, we need to assemble it. For stringified responses you can build a string. Alternately, it may be a good idea to push to an array and then use a buffer to assemble the result like [we do in the createServer article](https://blog.bearer.sh/create-node-http-server/).

Each time the `data` event fires we append to a string. Finally, when the `end` event fires we log the result. At the end of the call, we chain the `end()` method. This is a required part of the request, and without it the API call will not fire.

Let's look at another implementation of the same code.

```javascript
const http = require("http")

let options = new URL("https://postman-echo.com/status/200")

let myRequest = http.request(options, res => {
  // Same as previos example
  res.on('data' d=> {
    //...
  })
  //... etc
})

myRequest.on("error", console.error)
myRequest.end()
```

In this version of the code, we create a `URL` with our desired API endpoint. The `request` can now take this URL object as the first argument. We also assign the whole code block to `myRequest`. This gives `myRequest` the ability to control the request listeners rather than chaining them to the end.

You may be tempted to try and call `myRequest()`, but the action that fires the request is `.end()`.

### Shorthand requests with http.get

While `http.request` can handle all the major HTTP methods, `GET` has a dedicated shorthand method. It works exactly the same, except it accepts a url string, removes the need for `.end()`, and sets the method to `GET`. For example, our first example would look as follows with `http.get`:

```javascript
const http = require("http")

http.get("https://postman-echo.com/status/200", res => {
  let data = ""

  res.on("data", d => {
    data += d
  })
  res.on("end", () => {
    console.log(data)
  })
})
```

While not a major difference, this syntax makes handling `GET` requests easier.

## POSTing to an API

With common `GET` request handled, let's look at `POST`. The syntax is mostly the same.

```javascript
const http = require("http")

let body = JSON.stringify({
  title: "Make a request with Node's http module"
})

let options = {
  hostname: "postman-echo.com",
  path: "/post",
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Content-Length": Buffer.byteLength(body)
  }
}

http
  .request(options, res => {
    let data = ""
    res.on("data", d => {
      data += d
    })
    res.on("end", () => {
      console.log(data)
    })
  })
  .on("error", console.error)
  .end(body)
```

First we need to set up the body and options. Since we want to send JSON, we stringify the object and set it to `body`. Next, configure `options` with the necessary properties and headers. Note that we are telling the server the size of our payload with the `Content-Length` header, as well as the type of data with `Content-Type`.

The `http.request` portion looks mostly the same as in the earlier examples. We chain an `on("error")` listener. We also pass the body into the `end()`. This could also be written as `.write(body).end()`.

## Things to watch out for

While the code examples above are average use-cases, there are a few quirks to be aware of. Here are a few best practices and tips for working with `http.request`.

### HTTPS

In the examples we use `http`, but for secure connections requiring HTTPS, you can use the [`https` module](https://nodejs.org/api/https.html) in the same way. It is compatible with all the functionality of `http`.

### Watch for empty responses

Empty responses won't fire a `data` event. This means if your `response.on('end')` event expects something from the data listener, you may run into problems for some responses like those from redirects. Make sure to perform any checks before relying on data coming from the `data` listener.

### Make requests from inside the server

The `host` property in the options object defaults to `localhost`. This is nice for quick local experiments and instances where you want to call a server from within itself. For example:

```javascript

let server = http.createServer()

server.listen(3000, error => {
  http.request({
    port: 3000,
    path: "/endpoint"
  }, res => {
    // handle the response
  })
})
```

Using this technique, you can make calls upon the server from inside the `listen` method's callback.

### Error handling

One of the earlier examples briefly shows error handling, but it is worth mentioning again. The request itself, _not the response_, can fire an `error` event. You can listen for it by chaining `.on('error', (error) => {})` onto a request before calling `.end()` or if you have set the request to a variable, you can listen on the variable.

```javascript
let call = http.request(options, handleResponse)

call.on("error", handleError)
call.end()
```

This is also a good use case for [building a custom error type](https://blog.bearer.sh/customize-errors-in-node-js/) to handle specific responses.

### Cancelling requests

The `req.abort()` method allows you to cancel requests if used before the connection completes. In our example where the request is named `call`, this would be `call.abort()`.

## Wrapping up

So should you use `http.request` on it's own? In most cases it is a better choice to use one of the [many libraries available on NPM](https://blog.bearer.sh/top-node-request-libraries/). Especially one that supports promises and async/await. That said, know that beneath them all is the `http` module. You can take advantage of its features to built functionality on top of the requests that your application makes.

At [Bearer](https://bearer.sh), we use the underlying functionality of `http.request` to actively monitor and control calls made to third-party APIs and web services. Explore the rest of the [Bearer Blog](https://blog.bearer.sh/) for more on Node.js and interacting with APIs.

<!--kg-card-end: markdown-->