Architecture of a Single Page App with a REST API server
In this blog post, I will detail out how to build an complete web application, popularly called Single Page Apps(SPA) these days. At a high level, a SPA is one in which most of the work is done on the client side. Thus moving from page to page does not need full page reloads. It gets it's data from the backend , typically an API server, and renders it client side.
The stack I will be using is that of my project packageindex which can be found here.
NOTE: All code snippets for this post can be found in this gist. A sample puppet manifest code for deployment can be found here.
The block diagram of the architecture looks like:
The setup above represents one of the hundreds of possible ways of implementing it. Use this as a reference and a starting point, not a bible. Also, for simplicity, all this stuff runs on one machine. Typically in your production apps you may receive much more traffic so it may make sense to run different processes on different machines within a VPC or some other scale out strategy.
Nginx
This is the web server that receives all the requests. It's blazing fast and is fast replacing popular ones like Apache. All traffic to the site is over HTTPS. Requests to port 80 are redirected to port 443 (SSL). In order to set up SSL, you will need to have the appropriate certificates set up. See my older blog post for help with that.
Here is the nginx.conf that will let you get started quickly. It's a pretty standard config. A few things to notice:
A set of security related headers
After ssl termination, all requests are forwarded to Varnish running on localhost.
Varnish
Varnish is a web accelerator a.k.a. web cache. It caches requests and if there is a cache hit, services it directly without having to go to the upstream servers. It greatly improves performance by decreasing the load on your servers.
Varnish does not do ssl termination. Which is why it is done at nginx. Also, always ensure that varnish is listening on localhost(127.0.0.1) so that it is accessible only to the nginx running on your machine and not on all interfaces(0.0.0.0.)
Varnish is typically configured via the /etc/default/varnish file. I accepted all the defaults. I did add my own vcl file which you can find in the same gist
This varnish vcl tells varnish to cache all css/js and other static files for a day. All request are forwarded to an upstream node process.
Express (Node)
For serving the main site I used Express which is the most popular node base web framework. Using express is easy. Create the following directory structure in your favorite vcs e.g. git at $GIT_REPO:
web/
express_app.js
restify_app.js
routes/
index.js
api_module.js
views/
index.html
To start the node server:
node web/express_app.js
The express_app.js contains a bunch of url patters matched with the appropriate function to call. So we could match '/' to call routes.index which in-turn would render index.html and return it to the client.
Skeleton code is available in the gist.
One thing to notice is that it also matches a bunch of links of the form /api/v1/.... These are the API requests for data which is returned to the client as JSON. These requests are forwarded to a REST API server which actually returns the JSON data. (In a future post, I will explain how to make this REST API server available to third party devs via access keys/secret keys etc)
Also note that express runs on localhost.
In our example, the index.html contains angular code which is sent to the client the first time the site is loaded. Every time the client makes a request for data, it is forwarded to the REST API server and the response (in JSON) is sent back to the angular client which renders this data on the browser.
For cookies, the express app can forward any cookies received to the API server as a token header or something, so the REST server can know which requests are authenticated, which are not etc...
Restify REST API Server
The REST server is built using Restify. It essentially maps urls (associated with a HTTP verb) to a function which gets called and returns JSON.
Data Store - Redis
The Restify API server interacts with a data store. You can use anything you are familiar with. We used Redis. The point to note here is that the node express process never interacts with the redis data store. Any data it wishes to read/write from/to the data store is via the restify API server.
Make sure it listens only on localhost or something only the API server can access.
Angular.js client code
This is the client code which makes requests for data and it gets rendered on the browser. Describing the entire setup of angular is beyond the scope of this post, but you could google around for a bunch of tutorials that would teach you to write good client side angular controllers, services, directives etc.
Deployment
You can deploy your codebase to production via Puppet (or Chef). A simple puppet manifest to get you started can be found here. Control of the process is done via supervisord.
Hopefully the above post has helped you to understand what is involved in creating a SPA. To see it in action, check out PackageIndex

















