For this post I am going to discuss two of my favorites things: servers and modularity. Server design is an important skill for full stack developers and good server design can save a lot of time finding and fixing bugs. I recently started a personal project and, because I am anticipating some complexity on the backend, I decided to use a modular approach for my server. Lets look at each module and examine what it does.
The crux of my server is this server.js file:
1 2 3 4 5 6 7 |
|
This file is extremely simple: it requires express, create an app, passes the app into a config file and exports it. Why put such a small piece of code in its own file? The answer is separation of concerns. Each file does one job, and as a result we don’t have to think as hard about the different blocks of code and what they are doing. This tiny file actually does three very important things:
- Create an express server
- Send the server into another function to be configured
- Export the server for access by another operation
Now that we have a server we want it to listen for requests. The whole reason we have a server in the first place is that we have resources that we want to serve up to people (or machines) that request them. In this way a server is very much like a pharmacist. When people want medicine they go to the pharmacist, give her their request and their identification, and the pharmicist goes and gets their medicine for them. So of course we need our server listening for requests, otherwise its like a sleeping pharmacist. I take care of that step in index.js:
1 2 3 4 5 6 |
|
This file is even smaller than the last. I access the server I created in server.js by requiring that file, I get the port from the environment or set it to a default local port, and then I start my server listening for requests on that port. Again, it may seem silly to put these few lines in a single file, but now we have everything necessary to run our server in one file. You’ve probably been wondering about that require statement on line four of server.js, so we’ll go there now.
Why are we passing objects into a require statement? If you understand what a decorator is, really what we are doing here is just like object decoration. My config.js file exports a function that takes the server and express as arguments and runs various configuration methods on them, so any kind of code we need to configure our server goes in that function and will be applied we invoke it with a newly created app object. Lets take a look:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
There are some basic features that we want our server to have and those features are added here. At the top I require the two modules that my server needs: body-parser and express-session. The former appends a body object onto the request that contains form data from post requests as key value pairs, the latter adds a session object onto the request where I can store data about a particular user that will persist between requests. I need these services to store persistent data and process forms so I usually require them as part of my basic setup.
Next I define the function that I will export to configure my application. Recall that this is the function I will invoke with express and my server in server.js. After this I use the app.use() method to intialize the modules I required up top and import their functionality into my server. Next I use the express.static() method to define a directory where I will serve static files. Express is really smart: it sees the index.html file in that directory and will serve that to the client, along with any .js or .css files that index.hml requires, when a request is made to the base url. Finally, I pass the app into a routes.js file where it will be configured with all the specific routes I want to use for my api.
These are the most basic services I need to serve up a webpage. I make a server, add a couple useful features and tell it where my html is stored. Because I am using Angular, once the index.html file is loaded in the browser Angular’s router will take over the job of routing and serving files. But thats not all I need. A server can provide many kinds of resources to the client, and in my app I want it to provide access to a database. At the end of my config.js file I pass the app into router.js file, which looks something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
To define a server response to a given request you use “http verb” methods on the server object, each of which takes a url as its first argument and a callback as its second. The most common methods are app.get and app.post, the former makes a request for something from the server, either a web page or some data, and the latter sends data to the server. In routes.js I define responses for two different requests, each one defined by a specific url. When a post request is made to one of these urls Node.js will instantiate a request object and a response object and pass them into our callback where we can extract form data, store session data, access our database and define a server response. Now our server is fully functional, lets review each file and what it does:
- index.js –> starts the server listening for http requests
- server.js –> creates a server object with express
- config.js –> configures the server
- routes.js –> defines the routes to be handled by the server
The advantage of this organization is that we know exactly where to go when we want to change the configuration of our server or add new routes. We are saved from the confusion that results from having blocks of code that serve different purposes stored in the same file.