Array Service

A web service is a library of code that lives on the web. Clients call the functions of the library by issuing HTTP requests. Each function or endpoint has its own URL. Parameters may be appended to the endpoint URL or provided in the body of the request. Data is returned via an HTTP response.

You could write your own web service using Node.js's http library. You would have to parse the request URL and figure out what code to run based on its contents. Or you could use Express, a third-party package for making web services. At this point, your knowledge of web services is limited, so you decide to give Express a try as you build a service for sharing an array of data maintained by the service.

You create a new project and install Express with this command:

npm install express

This adds Express as a dependency in package.json. Starting up the service is similar to starting up a general HTTP server. You import the Express library, create a service, and begin listening on a designated port:

const express = require('express');

const service = express();

// The resource to share.
const array = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];

const port = 5000;
service.listen(port, () => {
console.log(`We're live on port ${port}!`);
});

Try starting up your service and visiting http://localhost:5000 in your browser. You should receive a 404 error because your service has no endpoint at this root URL. In fact, it has no endpoints at all. You must add some. You stop the service by typing Control-C in your terminal.

These are the ones you dream up:

You add endpoints to an Express service using callbacks. These callbacks are registered using one of several functions: get, post, patch, or delete. If an endpoint is only reading data from the service and not modifying any resource, then the callback is registered with the service's get method. Your first endpoint looks something like this:

const express = require('express');

const service = express();
const array = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512];

service.get('/all', (request, response) => {
// Respond to request...
});

const port = 5000;
service.listen(port, () => {
console.log(`We're live on port ${port}!`);
});

As with the HTTP server, the callback receives both request and response objects. Try running your service and visiting http://localhost:5000/all. You find that nothing loads because the service never sends a response. You want to send the array back in the response. The body of an HTTP response is text, so you convert the array to JSON:

service.get('/all', (request, response) => {
response.json(array);
});

Try accessing the endpoint now. You should see the array.

With a web service, you are effectively making a function call. The function just happens to be running on some other computer. Like a regular function call, you have parameters and return values. One difference is that you do not have exceptions. To communicate errors, many web services provide a status field in the returned JSON, and you decide to do the same:

service.get('/all', (request, response) => {
response.json({
ok: true,
result: array,
});
});

There's no way for this endpoint to fail, so the ok field is always true.

Your one endpoint needs a parameter in order to do its job. Any component of the endpoint URL that starts with a colon is treated as a parameter. You therefore use /one/:index for your URL. The actual index value supplied by the client is available to you inside the request.params object. Putting all this together, you write this for your second endpoint:

service.get('/one/:index', (request, response) => {
const index = parseInt(request.params.index);
response.json({
ok: true,
result: array[index],
});
});

The parameters always arrive as strings. You convert the index to a number before doing anything else with it. Try accessing this endpoint via the URL http://localhost/one/3. Do you get back the value at index 3?

But what if the index is out of bounds? You add a check for bad indices:

service.get('/one/:index', (request, response) => {
const index = parseInt(request.params.index);
if (index < 0 || index >= array.length) {
response.status(404);
response.json({
ok: false,
result: `illegal index: ${index}`,
});
} else {
response.json({
ok: true,
result: array[index],
});
}
});

The returned object provides a description of the error. Additionally, the HTTP status code is set to 404, which tells the client that the specified index isn't legal. Try visiting this endpoint in your browser and passing various indices. What happens if you provide no index at all?

Your endpoint for sub is similar to one, but it needs two parameters, and it calls the Array.slice method to extract the subarray:

service.get('/sub/:fromIndex/:toIndex', (request, response) => {
const fromIndex = parseInt(request.params.fromIndex);
const toIndex = parseInt(request.params.toIndex);
if (fromIndex < 0 || fromIndex >= array.length) {
response.status(400);
response.json({
ok: false,
result: `illegal from index: ${fromIndex}`,
});
} else {
response.json({
ok: true,
result: array.slice(fromIndex, toIndex),
});
}
});

Try visiting this endpoint in your browser and passing various indices. What happens if you pass negative or large values for the to-index?