Serverless is easier than you think
When serverless pops up in conversation, there is sometimes an uncomfortable silence in the room. This is possibly because the majority of us don’t know much about serverless. Hopefully, you’ll know more after you read this post!
Serverless is the new paradigm for building applications. This doesn’t mean that there are no servers-they will still be somewhere, but they just aren’t managed or owned by you. Instead, the infrastructure is managed by either the cloud vendor or the third-party services we are consuming. As a result, we only have to think about our code, architecture and which services to use.
OK, OK… this sounds great, but there must be a but, right?
Yes, the but is that it requires a change of mindset, not just at the engineering level but company wide. Engineers need to start thinking serverless and use existing services instead of reinventing the wheel, DevOps need to understand the cloud architecture and learn new ways of dealing with the new infrastructure, management needs to understand the benefits, risks and dependencies of being in the cloud, and the C level needs to be aware that there will be a learning curve, a migration, and an initial cost before the business can be more productive and long-term Operational Expenditures (OpEx) can be reduced.
In this post we’d like to tell you about one of our experiences with serverless, specifically:
- What we built and why
- The delivery pipeline
- Conclusions about serverless in general
What we built and why
We’ve been developing serverless solutions for more than a year now and we‘ve delivered a serverless web application called Made For Serverless, which helps developers choose the right tools for their solutions.
In this web application the user is able to suggest the addition of new tools to the catalog, find tools based on certain criteria, and review tools if registered. There’s much more to come in future releases.
We believe that developing a serverless application should be easier, but until it is, we want to put in our 2 cents by showing that a fully serverless web application can be developed using just a few AWS services.
We decided to build a static web application using React and written in TypeScript. React is a UI library well-known to developers that allows new comers to get onboard quickly due to its massive quantity of libraries and documentation and the ease of reusing components across the application. In addition to React and TypeScript, we added Redux to handle states, AWS Amplify SDK for dealing with User Auth, Axios for our HTTP requests, React Router for routing our application and ReactStrap to make our UI styling very easy and cool.
The code was structured following React Atomic Design, which really makes you think about whether a component can be reused.
As you might expect in this kind of architecture, we delegated all the heavy logic to the backend, which were basically endpoints pointing to AWS Lambda functions. We will talk more about these endpoints later on.
While it may be terrifying to think about a server hosting a website, certificates or caching, for example, we decided to go for a serverless approach to hosting by using AWS S3 with static hosting. Moreover, we enforced HTTPS and caching through AWS CloudFront, a cloud-based Content Delivery Network. AWS Route53 is in charge of directing our domains to the CloudFront endpoint. Together, Route53, CloudFront and S3 help us achieve low latency and high availability as well as another layer of defense against attacks given by AWS Shield.
Furthermore, we used Serverless Framework to automate the deployment of our frontend, making it very easy to deploy the web application to different environments.
When you’re attempting to build a serverless backend for your application, we recommend that you explore the services are already available in the market. Avoid reinventing the wheel at all costs, unless you have a very good reason to do so. For example, AWS has services like Cognito, Polly, S3, DynamoDB, Lambda, SNS, API Gateway, Rekognition, and many more that are widely used in serverless architectures.
In our case, we used AWS DynamoDB, Cognito, API Gateway, SNS and Lambdas. It is well worth mentioning that Lambdas take on a lot of responsibility, as they are key in these kinds of architectures. In most cases, they act as glue between services.
We used AWS DynamoDB, a highly scalable, highly available NoSQL database, as our data store. It is configured to scale
on demand and we created
Secondary Global Indexes to improve the efficiency of the queries. Moreover, some additional features like DAX enables caching, making this database perform even better.
AWS Cognito is in charge of our web application user management and auth through Cognito User Pools. When a user registers in our web application, the registration information is recorded in our Cognito User Pool. This auth information can then be used by other AWS services. We take advantage of the JWT token generated by Cognito in some parts of our application.
AWS API Gateway and AWS Lambda almost always go together in our implementations: they play a big part in our serverless backend. API Gateway will expose endpoints that are then mapped to our Lambda functions. Furthermore, for some endpoints the API Gateway will call an Authorizer function, which will check auth data against Cognito before allowing access to functions. However, API Gateway is not the only thing that can trigger a Lambda function. Below are another two scenarios where we use functions.
1. An SNS event is fired when a tool is rated by a user. This SNS event, which contains information about the user tool rating, then triggers a Lambda function that will update the average tool rating on DynamoDB.
2. When a user verifies the account in the application through the code sent to the email address, an
Account Confirmedevent is fired which triggers a Lambda function that will assign the user to the group of Contributors
We currently have more than 15 functions deployed that are written in TypeScript and bundled with WebPack. Our usual approach is to leave the least amount of code in the handler and create services, utils, models, etc. which facilitates testing and allows us to migrate the same function to another cloud provider. Additionally, there are a lot of awesome tools out there that can be integrated with Lambda functions to improve security, cost management, or monitoring.
Similar to the frontend, our backend uses Serverless Framework to deploy the stack with just one command. This allows us to create several replicas of our production environment for testing in just a few minutes. The same result could also be achieved using AWS CloudFormation, which is what Serverless Framework uses under the hood, but Serverless Framework really makes the process easier.
In this project we’ve automated as much as we can. That means that our CICD pipeline will deploy a new version of the application to the Dev or Production environments once we merge our Pull Requests into respective branches.
A lot of this automation is made easy thanks to Serverless Framework and the amazing
sls deploy command. We have also chosen CircleCI for our pipelines. Furthermore, we integrated GitHub with CircleCI to kick off build and test jobs when a Pull Request is created in the repository.
Serverless applications can be beneficial in many circumstances, allowing products to go live faster and making it possible to test the market sooner. Additionally, the ability to deploy replicas of your production environment in a few minutes is incredibly powerful. Moreover, serverless can increase the productivity of developers/software engineers by letting them focus on what really matters: which is solving real business problems and making use of existing services for operations that are not business mission critical.
On top of that, we think that serverless is here to stay because of its ability to build complex architectures in a relatively short time, its potential for automatically scaling on high demand, its costs structure, which is based on usage, and the fact that it is possible to perform A/B testing or make changes to the logic of functions in isolation with zero downtime.
That’s the short version of the story! We hope it’s given you a good overview, so the next time the topic of serverless pops up at a cocktail party, you’ll be able to step into that silence and join the conversation. Or start a conversation here — tell us more about your experiences with serverless in the comments.
Big thanks to The Agile Monkeys for proof-reading and giving feedback ❤️
If you have questions about the project or the architecture, you’re planning to build a serverless application, or you’re having trouble with a serverless project, please contact us!