Mutahhir Hayat Central

Spatis: What would it take to serve some HTML?

January 17, 2020

In the previous post we talked about the requirements for Spatis, and my plan is to build it out breadth-first. What would that mean? Basically I get the really basic stuff working first, and then when I have a basic structure going, start cleaning up, expanding, fixing and building it up.

For Spatis, it would look like:

  1. Getting a basic NodeJS project started
  2. Writing down a basic example script
  3. Writing some core pieces like HTTP server and be able to serve rendered react.

In all fairness, this is not implementing the base layer for all that Spatis needs to do. In order to come close to that, we’d have to take on additional requirements:

  1. Have a basic routing for front end that mirrors the server side routes
  2. Have a simple API layer that allows for state to be communicated with the server.

If you’re familiar with modern web development, you would get a hint as to why I’ve separated the last two points. Those steps are at the core of how Spatis is different and are not particularly trivial to code out. Adding that to the proof of concept would increase significant development time.

When dealing with large projects with thick fog of war, it is often preferable to start and put a stake on the ground before you delve into solving all the problems you foresee. This has advantages in morale, and if you’re a somewhat tactile driven person like I am, thinking with your hands by seeing how things are visualized.


Let’s see how something like this would look like. The simplest NodeJS server that can serve up HTML looks somewhat like so:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

This uses the NodeJS library http to listen on port 3000 on localhost or 127.0.0.1, also known as the loopback IP address. When a browser tries to visit http://localhost:3000/, it serves up a plain text document with the content Hello world. That’s all there is.

The above, however, is not really extensible. Here’s how the server would look like if I extended the above to be able to serve two URLs.

const http = require('http');
const {URL} = require("url");

...
... 

const server = http.createServer((req, res) => {
  const {url} = req;
  const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
  
  const {pathname} = parsedUrl;
  
  switch(pathname) {
    case '/': 
			res.statusCode = 200;
		  res.setHeader('Content-Type', 'text/plain');
		  res.end('Hello World');
		  break;
	  case '/foo':
	    	res.statusCode = 200;
		  res.setHeader('Content-Type', 'text/plain');
		  res.end('Foo Bar');
		  break;
	  default:
	    res.statusCode = 400;
	    res.setHeader('Content-Type', 'text/plain');
	    res.end('Not Found');
  } 
});

...
...

I admit that I haven’t cleaned up this code for the sake of making a point, but even cleaned up, there’s a lot more we have to do to make it into a nice, usable API. To compare, let’s see what the above would’ve looked like in Express:

onst express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => res.send('Hello World!'));
app.get('/foo', (req, res) => res.send('Foo Bar'));

app.listen(port, hostname, () => 
  console.log(`Server running at http://${hostname}:${port}/`));

The difference is stark, and developing on Spatis needs to be closer to the Express model that the NodeJS model. If Spatis were to start with the NodeJS model as base, what kinds of pieces need to be created to make it closer to Express?

  • An easy to use routing API: In express it was app.get('/', <handler>) which allows the developer to say, ‘When the browser makes a GET request on the root url (/), invoke handler‘.
  • Wrappers around the request req: The request object represents all the information the server has about a web request. Express further enriches it by parsing the url, query parameters, cookies, and adding APIs to make common tasks easy.
  • Wrappers around the response res: The response object represents what the server will send back to the requester. While the NodeJS response object is pretty capable, Express adds on convenience methods for sending specific kinds of data, and groups common operations into single APIs.

That being said, another popular web server library is Koa. The driving goals for this library are to make it minimal and do away with NodeJS ‘callbacks’1. Here’s the same example in Koa:

const Koa = require('koa');
const router = require('koa-router')();

const app = new Koa();

router.get('/', async (ctx) => ctx.body = 'Hello world');
router.get('/foo', async (ctx) => ctx.body = 'Foo Bar');

app.use(router.routes());

app.listen(3000);

The Koa example is a bit more involved, but that’s because of its desire to keep things minimal. In order to use a routing mechanism, you need to add that as a ‘middleware’. Middleware is a common term in web servers to denote steps between receiving a request and sending a response. For example, if you wanted logging for every request you receive, you would need to add a middleware for logging. The middewares get called for each request in the sequence you add them to the server usually, so you can do things like error handling if your routes threw exceptions by adding the error middleware after your routes are defined.

Now that we have an idea of how modern web server development looks like, it’s time to decide what Spatis’ API would look like.

One thing that I’ve been considering is whether I can utilize Koa’s flexibility to build Spatis. The upside is that I get a lot for free as Koa is a stable production proven library, but the downside is that people would have to install Koa alongside Spatis and know the nuances of Koa in order to make Spatis work.

I might consider making Spatis into a middleware for Koa down the line, but I think Spatis might not fit with what Koa envisions it’s middlewares to do. Spatis is more of a full-stack middleware, and while there’s a path now, there’s a chance that conflicting ideologies of the projects will cause problems due to design constraints. Thus, Spatis is on it’s own for now.


  1. You can find more about callbacks here, but essentially callbacks as javascript functions passed as parameters to functions and are expected to be called asynchronously at a future time.


Mutahhir Hayat

Written by Mutahhir Hayat.
Should be doing other things, keeps coding instead
Twitter?