Serving from Files

Being able to write and run JavaScript on your server is nice. Most servers will want to vend out and process content living in files, too. Your next job is to make your server read files from a subfolder and send them in a response back to the browser. This is similar to what browser-sync was doing for you in earlier chapters.

Create a new folder named app inside of your chattrbox project folder. In it, create an index.html file with the following text:

Hello, File!

You do not need any actual HTML in this file – it only needs some content that can be read. Your project folder should look like Figure 15.11.

Figure 15.11  Chattrbox project layout

Chattrbox project layout

Reading a file with the fs module

In index.js, import the Node.js file system module, fs, and call its readFile method.

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
  console.log('Responding to a request.');
  res.end('<h1>Hello, World</h1>');
  fs.readFile('app/index.html', function (err, data) {
    res.end(data);
  });
});
server.listen(3000);

The readFile method takes a file name and a callback. Inside your callback, you sent the contents of the file instead of the HTML text using res.end.

Notice that your callback accepts an err argument as well as the data from reading the file. This is a Node.js programming convention that we will discuss later in this chapter.

nodemon should have restarted your program, so you can go directly to your browser and reload. In your browser, you should see exactly what you wrote in your index.html file:

Hello, File!

This is a good start, but your chat application will need to do more than serve a single HTML file. That HTML file may request other CSS or JavaScript files. To fulfill those requests, your node program will need to understand what file is being requested and where to look for the requested file. You will work on that next.

Working with the request URL

First, you need to get the URL path from the request object. If the path is just '/', it is best to return the index.html file. This is a common convention from the early days of the web.

Otherwise, you should try to return the file the request object is asking for.

In index.js, update your callback to check what file the browser is requesting.

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
  console.log('Responding to a request.');
  var url = req.url;

  var fileName = 'index.html';
  if (url.length > 1) {
    fileName = url.substring(1);
  }
  console.log(fileName);
  fs.readFile('app/index.html', function (err, data) {
    res.end(data);
  });
});
server.listen(3000);

Using the request object’s url property, you can see whether the browser is asking for the default (index.html) or another file. If it is another file, you call url.substring(1) to strip off the first character, which will be a '/'.

For now, you are just logging the filename to the console.

After nodemon restarts your program, try going to http://localhost:3000/woohoo or any other path, including the default '/' path. The results in your terminal will look something like Figure 15.12.

Figure 15.12  Logging the requested file path

Logging the requested file path

(Recall from Chapter 2 that browsers will automatically ask for a favicon.ico file, so you may see a request for it logged to the terminal as well.)

Now it is time to make use of this path information.

Using the path module

You could just pass the fileName to fs.readFile, but it is better to use the path module, which has utilities for handling and transforming file paths. One small but important reason for using the module is that some operating systems use a forward slash and some use a backslash. The path module handles these differences with ease.

Update index.js to import the path module and use it to find the file that was requested.

var http = require('http');
var fs = require('fs');
var path = require('path');

var server = http.createServer(function (req, res) {
  console.log('Responding to a request.');
  var url = req.url;

  var fileName = 'index.html';
  if (url.length > 1) {
    fileName = url.substring(1);
  }
  console.log(fileName);
  var filePath = path.resolve(__dirname, 'app', fileName);
  fs.readFile('app/index.html'filePath, function (err, data) {
    res.end(data);
  });
});
server.listen(3000);

Test a few filepaths in the browser to make sure your application still works the same. The default path should return index.html, and nonexistent paths (like '/woohoo/') should show nothing and log out their filename.

Next, create a test.html file in the app folder. Write the following inside it:

Hola, Node!

Try to access it in the browser. Your node program should return it without any trouble (Figure 15.13).

Figure 15.13  Retrieving test.html

Retrieving test.html

You have added code that successfully serves a specific file based on the URL path. The next thing to do is abstract out that functionality into its own module.

Creating a custom module

Your callback has (at least) two jobs. It figures out what file is being requested, and it reads that file to send back in the response. To make the code a bit more modular and maintainable, one of those responsibilities should be moved to its own module.

In CoffeeRun, you declared modules in an IIFE that assigned a value to a property of the global namespace. Modules in Node programs work differently. You still write your module code in a file by itself, but you do not need the IIFE.

Create a new file called extract.js in the same directory as your index.js (not in the app directory). Add a function called extractFilePath that finds the appropriate file. (This code is very similar to what you already wrote in index.js.)

var path = require('path');

var extractFilePath = function (url) {
  var filePath;
  var fileName = 'index.html';

  if (url.length > 1) {
    fileName = url.substring(1);
  }
  console.log('The fileName is: ' + fileName);

  filePath = path.resolve(__dirname, 'app', fileName);
  return filePath;
};

You have taken much of the code from index.js and placed it in its own function, called extractFilePath. Next, make the extractFilePath function available so that other modules can import it with require. To do this, assign extractFilePath to a global variable named module.exports. This is a special variable provided by Node. Whatever value is assigned to it is the value other modules are able to import. Any other variables or functions will not be visible to other modules.

...
  filePath = path.resolve(__dirname, 'app', fileName);
  return filePath;
};

module.exports = extractFilePath;

This new line tells Node that when you import the extract module by calling require('./extract'), the value returned is the extractFilePath function. Do that now in index.js.

Using your custom module

Update index.js to use your new extract module instead of handling those responsibilities.

var http = require('http');
var fs = require('fs');
var path = require('path');
var extract = require('./extract');

var server = http.createServer(function (req, res) {
  console.log('Responding to a request.');
  var url = req.url;

  var fileName = 'index.html';
  if (url.length > 1) {
    fileName = url.substring(1);
  }
  console.log(fileName);
  var filePath = path.resolve(__dirname, 'app', fileName);
  var filePath = extract(req.url);
  fs.readFile(filePath, function (err, data) {
    res.end(data);
  });
});
server.listen(3000);

You imported your custom module using the require function. You assigned the value of the module to the new variable extract. Now you are able to use the extract function just as you would the extractFilePath function.

After nodemon has reloaded your code, test some URL paths and confirm that the default index.html and test.html still load. Also, make sure that nonexistent paths come up as a blank page and without an error.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.235.144