Tuesday, March 19, 2024

Serve External Content from Your Express.js Apps

Cross-Origin Resource Sharing (CORS) is a mechanism which allows clients and servers to communicate even if they are not on the same domain. Since it was accepted as a W3C Recommendation back in January 2014, developers have indicated that CORS is not as simple as specifying the appropriate server URL. There are a lot of changes necessary on both the server and client tiers to get it working. A much simpler approach is to proxy your HTTP requests via a server running on your domain. As we’ll learn in today’s article, this can be done with relatively few lines of code by combining the power of the express.js library with one of several excellent Node.js libraries. This tutorial will present how to serve up weather information from a public API using the request, fetch, and request-promise-native libraries.

The Dark Sky API

The Dark Sky Company specializes in weather forecasting and visualization with down-to-the-minute precision. In addition to the website, they also offer the Dark Sky API. You can use it for free, up to certain volume. All you need to do is register to receive a unique API key.

Example 1: node-fetch

The Fetch API allows you to make network requests similar to XMLHttpRequest (XHR). The main difference is that the Fetch API uses Promises, which enables a simpler and cleaner API. Node offers its own implementation, called node-fetch. Here’s an Express server file that accepts requests over port 8081 and returns all of Dark Sky’s weather information on Los Angeles. It includes all sorts of information, including:

  • Apparent (feels-like) temperature
  • Atmospheric pressure
  • Cloud cover
  • Dew point
  • Humidity
  • Liquid precipitation rate
  • Moon phase
  • Etc…

Note that this example converts the response to a proper JSON object:

const express = require('express');
const app     = express();
const fetch   = require('node-fetch');

app.get('/', function (req, res) {
    var url = 'https://api.darksky.net/forecast/<API KEY>/37.8267,-122.4233';
    
    fetch(url)
    .then(res => res.json())
    .then(data => {
        res.send({ data });
    })
    .catch(err => {
        res.send(err);
    });
});

const server = app.listen(8081, function () {
    const host = server.address().address
    const port = server.address().port
   
    console.log("Example app listening at http://%s:%s", host, port)
});

Example 2: request

Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default. It employs the pipe() method to pass the response stream to other objects, such as a file stream, or to the server Response:

var express = require('express');  
var app     = express(); 
var request = require('request');
 
app.get('/', function(req, res) {  
    var url = 'https://api.darksky.net/forecast/<API KEY>/37.8267,-122.4233';
    req.pipe(request(url)).pipe(res);
});

const server = app.listen(8081, function () {
    const host = server.address().address;
    const port = server.address().port;
   
    console.log("Example app listening at http://%s:%s", host, port);
});

Example 3: request-promise

The request-promise library provides a Bluebird-powered HTTP request client with Promise support. The code presented below employs the request-promise-native variation, which uses native ES6 promises.

The request promise object accepts an options object that contains things like the uri, header, and a flag to automatically parse the JSON response. The call to the request promise instance follows the call(options).then(parsedBody).catch(errors) pattern of promise chaining:

const express = require('express');
const app     = express();
const rp      = require('request-promise-native');
const options = {
    uri: 'https://api.darksky.net/forecast/<API KEY>/37.8267,-122.4233',
    headers: {
        'User-Agent': 'Request-Promise'
    },
    json: true // Automatically parses the JSON string in the response
};

app.get('/', (req, res) => {
    rp(options)
    .then(parsedBody => {
        res.send(parsedBody);
    })
    .catch(err => {
        res.send(err);
    });
});

const server = app.listen(8081, () => {
    const host = server.address().address
    const port = server.address().port
   
    console.log("Example app listening at http://%s:%s", host, port)
});

The npm CORS Package vs. Proxies

You may have come across the npm CORS Package on the npm site. This package is geared towards enabling your server-side application components to serve content over Ajax to clients that reside on a different domain. This usage differs from the above examples, which are fielding requests from clients on the same domain but returning content from other sites. We’ll be taking the npm CORS Package for a spin next month.

Conclusion

Cross-Origin Resource Sharing (CORS) need not require a lot of changes on your server and client tiers to get it working. Node.js apps can easily proxy your HTTP requests from your server to third party sites and resources. With so many good libraries around, which one you use is more a question of coding style than capabilities.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured