Serverless: Moderate fun with Modular Functions in Node.js

1,467 reads

Build and deploy Javascript modules efficiently to serverless clouds, in a pure functional and composable way.

I love Node.js. I’ve re-discovered Javascript through it, and I’m never going back. I also love Express. It makes writing server applications so simple, and its Connect-based middleware stack approach makes extending and composing applications easy and fun. Node.js, combined with frameworks like Express, is great for microservices. Thanks to its lightweight character, non-blocking nature, and quick development experience, it beats (imho) other microservice technologies such as Spring Boot. Couple it with Docker and the sky’s the limit (…as long as you allocate enough resources to your container cluster, that is).

Smaller than small

First they gave us Servers, so we built Service-Oriented Architectures.

Then they gave us Containers, so we built Microservices.

Now they give us Event Handlers, so we’ll build Cloud Functions.

As we made our hosting platforms more amenable to deploying smaller units, so have our applications broken down into smaller software packages. There are many reasons for this, and there are diverging opinions on whether it’s a good thing. But if we look back at the original concepts behind cloud computing, there was a dream of having code distributed infinitely in a network of connected computation nodes. And we’re getting a little closer with the emergence of serverless platforms.

But not too small

We cannot go too small because we have blocks (procedures) that consist of sequences of computational steps that require shared memory in order to execute effieciently. Then we wrap those around a function definition, that defines a contract for its input and output, which allows it to be composed with other such functions.

This approach has been very successful in the architecture of Unix, and is one of the reasons for its longevity and ubiquity. I don’t mean to suggest that Web applications should follow a comparable cloud-based shared eco-system (although some are trying), but I do believe that we could benefit from applying similar principles when building Web applications.

Beyond function definitions, we also group closely related functions in modules. An example would be the CRUD operations for data within a given domain (e.g. user management). Those tend to share code, such as common object models, parsing logic, formatting, etc. So if were to deploy individual functions in serverless environments, we’d end up with a bunch of duplicated code.

But let’s assume we don’t mind duplicated code deployments. After all, we can still manage it properly in our code repositories. We might still want to share temporary resources, though, such as database connections. We might also want to make sure that all operations for the same domain are deployed and managed as a single unit.

It fits well with the Single Responsibility Principle:

Gather together those things that change for the same reason, and separate those things that change for different reasons.

Going Serverless

So, Node.js is great for Microservices. And it’s also great for writing smaller function modules. And Express is great for building Web application in Node.js.

However, most of these serverless environments (AWS Lambda, Google Cloud Functions, etc) already provide a lot of facilities out of the box to handle the common functions of a Web server. And for these Nanoservices, which should not have more than a handful of functions, we really shouldn’t be bothered with the overhead of complex Web server logic. We should certainly leverage HTTP, as it is the ubiquitous transport mechanism for transfering messages between Web services, but we should do it in a more RPC kind of way.

This where most current frameworks offer too big of a tool for the small footprint that we’re looking for. If anything, I’d argue that going serverless should allow us to free ourselves from frameworks, and focus instead on building purer functions. There is, however, still a need for basic routing within a Nanoservice in order to map incoming request to the appropriate handler function. Also, because of the proprietary nature of these commercial serverless environments, there is a case to be made for a certain level of abstraction, so that our functions can be decoupled from the specifics of the platform they’re executed in.

I think that more applications will surface soon that are focused on enabling functional programming applied to serverless deployments, which I’m very hopeful about because it feels like a step in the right direction. Certainly there are many real-world considerations still to be addressed, such as latency, performance, memory usage, etc. But just like with Microservices, I believe that we’ll find the right set of tools and practices to make this, not just practical, but also highly performant on real-world applications.

Modular Functions

I wrote a small package to address these needs when I started deploying serverless applications: modofun.

It carries no additional dependencies, because we want our deployments to be as small as possible, and it’s limited to about 100 lines of code. It aims to add just enough (and not any more) functionality to make it easier to deploy function modules on serverless platforms, with the added benefit of leveraging existing middleware built for existing frameworks like Express. Here are a few of its features:

Basic routing to functions

Parameter parsing

Automatic HTTP response building

Support for ES6 Promises (or any other then-able)

Connect/Express-like middleware support

Google Cloud Functions

AWS Lambda (with AWS API Gateway events)

(with AWS API Gateway events) Automatic error handling

It makes it easy to expose an existing module as Serverless cloud functions:

An intentionally simplistic router maps incoming requests to functions, and passes the additional components of the URL path as function arguments. Additional request data is also available as context (this) for the function invocation.

When applying modofun to a function module, you can also re-use existing middleware components to be executed everytime, or only for certain functions (more details in the documentation). It then returns a handler for events generated by the serverless platform (currently supports AWS and Google Cloud).

Get it with npm:

npm install modofun

You can find more examples and detailed documentation in the official website: https://modofun.js.org. You can also find the full source code on GitHub: https://github.com/modofunjs/modofun.

There’s also a real-world example that includes exposing a GraphQL endpoint, a Google Cloud Functions deployment using the alternative Express-like request/response mode, and an AWS Lambda deployment. All using modofun, combined with Babel and other cool technology.

Tags