DEV Community

Md. Ishtiaque Zafar for FINN

Posted on

Why I don’t like API frameworks together with Serverless

This blog was originally published here

Every time when creating a new API that is meant to be serverless, I consider or have been asked to consider using an API framework together with a serverless application framework like Serverless Framework or AWS SAM. So far, I have found none that impress me. Here’s why:

They do not promote modularity

If you have a look at frameworks like serverless-http or nestjs + serverless, or at zappa + flask for python lovers, all of them follow the same pattern:

  1. Instantiate an app
  2. Add routes and associated handler functions to the app
  3. Add middlewares for authorisation, logging, etc, in the app itself
  4. Bundle everything into a single lambda function which gets triggered with the base path ANY /

How your deployed code would look with an API framework
How your deployed code would look with an API framework

You see how heavy-weight it can be. It’s like packing your monolith app into a single lambda function and executing it. Heavier code will lead to longer boot times and hence longer response time compared to the no-framework approach. Poor lambdas are not meant to be used in such a way. You might get away with small apps, but if your monolith is huge, it’s better to use some other solution like containerisation with ECS.
A better solution, in my sense, should be something like this:

Without any API framework
Without any API framework

Every lambda function contains just your business logic and code needed to interact with data sources. Such lightweight, much wow, very fast.

Why not offload overhead actions elsewhere?

An API does not just include your business logic, it needs to have some other must-have functionalities, like routing, input validation and authorisation, to name a few.

Most of these API frameworks come from times before the serverless revolution, and were designed to create API services to be run on “dumb” infrastructure, dumb in the sense that they were just compute resources, not capable of anything else like routing, request authorisation, etc. Hence, everything had to be done (and written by you!) in the code.

Going truly serverless means that you break your system into parts and then use readily available managed services which take care of individual responsibilities. For example, routing and input validation can be done by the API Gateway. You can have a custom lambda authoriser for authenticating all your requests. For your business logic, you can have lambda functions small enough to focus on one topic per function.
Have a look below:

A better serverless system with offloaded responsibilities

Adds unnecessary learning curve

They add an unnecessary learning curve for the team. A new engineer on the team needs to learn the new framework as well. So why introduce one more thing to learn for your team, when writing small functions is the ultimate goal.

Take django for example. This framework has such a high learning curve and is somewhat opinionated. The goal of lambda functions was to enable developers to execute arbitrary functions, not entire apps, in a lightweight container-like solution. Read that again: “…functions, not entire apps, in a lightweight container-like…”. Using just the Serverless Framework or AWS SAM gives you so much flexibility and speed.

Downsides

One downside I have noticed working with a barebones framework is that the code is non-standard. Each project uses the Serverless framework, but the code structure is different in each one of them. Here are three different structures I’ve worked with recently:

Different folder structures

You can see clearly how vastly it varies, mostly depending on the team and it might add to the onboarding time. As said earlier, this is a tradeoff against learning an API framework and the heaviness that comes with it. This can be easily tackled by following the same core principles which these API frameworks use.

Another downside of hyper-granularity is that you end up having many functions that share significant portion of the same code, and now you have that many more cold-starts.

Conclusion

In tech, no solution is the perfect solution, every single one of them comes with tradeoffs, so it’s up to you to decide which ones you can live with.
Got different opinions? I would love to discuss them. Drop them in the comments below!


If you liked this article, sharing it in your network would help a lot!

Connect with me on LinkedIn or Twitter or Medium

Discussion (0)