Infrastructure as Code only works as Code…
By: Frédéric Barthelet
Tags: Infrastructure As Code, Serverless, AWS, Typescript, Serverless Architecture

The popular Serverless framework allows you to use YAML or JSON to describe and version your infrastructure configuration. The serverless.yml or serverless.json file stored at the project root is used by default to provision any project using this framework.

This is no revolution in the IaC frameworks world, where most of them rely on declarative file syntaxes to describe infrastructure.

However, a lesser known and recently introduced feature allows you to use a serverless.js or serverless.ts file as the default configuration file. In this article, I’ll describe the advantages of using such a format to build serverless applications faster and with a better developer experience.

TL;DR

You can use serverless.ts service file in the Serverless framework. You can benefit from:

Types : you can use service file definition types to get quick feedback on available properties for each block. However, the framework is not written in TypeScript, and so the definitions can sometimes be outdated. Regular community maintenance is required.

: you can use service file definition types to get quick feedback on available properties for each block. However, the framework is not written in TypeScript, and so the definitions can sometimes be outdated. Regular community maintenance is required. Imports : JavaScript file imports allow you to split the definition file into multiple files. This means you can have fine-grained function block definitions right next to your handler’s codebase.

: JavaScript file imports allow you to split the definition file into multiple files. This means you can have fine-grained function block definitions right next to your handler’s codebase. References: you can write custom-made functions to build AWS intrinsic syntax and navigate easily through infrastructure definitions, dependency by dependency, using the native click and follow features of your IDE.

TYPING

The TypeScript and Serverless communities joined forces to lay the groundwork for Serverless framework types, which allow direct feedback when writing a Serverless service file.

If you want to give it a try, just run serverless create --template aws-nodejs-typescript in a new directory (and make sure you’re using at least version 1.75 of Serverless framework to benefit from serverless.ts service file definition). The new Serverless type encloses all available configuration keys for the framework — which means you don’t need to go through the full serverless.yml configuration example to get the right syntax.

VSCode type suggestion for Serverless service file

As the framework evolves, all the definitions are maintained by the community within the DefinitelyTyped repository. Unlike the recently-added JSON schema validation, which was made directly within the framework source code, the definitions require constant improvement to follow the evolution of the service file definitions. Please report any issues you encounter, to make sure this definition stays up to date.

IMPORTS

JSON and YAML file formats cannot be split into multiple files. In order to avoid large service definition files when using those formats, Serverless came up with a dedicated variable resolver, allowing the use of the ${file(filepath)} function to import content from other files.

This can be leverage for example for function definition. An all-in-one serverless.yml file containing the entire service definition:

can be split in multiple files:

The problem with this approach is that you rely on the string definition of the other files to be accurate. This can result in bugs when a file path contains a typo, or when the source file is moved. It also impacts developer experience, because those links cannot be resolved by your IDE of preference.

Switching to JavaScript and TypeScript removes this issue. Dependent files are imported, and their links are usually dynamically updated whenever you change your project directory structure, thanks to your IDE. Any developer can click and follow referenced files to dive into specific service configuration blocks.

Using the power of Javascript imports, we can now keep function definition much closer to function handler code for complex Serverless applications :

As you see above, both function configuration and handler are within the same file. It considerably speeds up development, as no navigation is required between files to develop an end-to-end feature. Both execution context and instructions are located within the same easy-to-access file.

REFERENCES

You often need to inject specific attributes of provisioned infrastructure into other pieces of infrastructure within your application. For example, your Lambda handler’s code may rely on the DynamoDB table name to do the required feature.

The usual way of doing this is to use AWS native intrinsic functions throughout your serverless.ts configuration file. You inject AWS CloudFormation native syntax blocks into your service file’s resources property, and those services are provisioned together with your functions. You can then use the Ref intrinsic function to inject the generated DynamoDB table as one of your lambda’s environment variables in create.ts .

The problem with this syntax is the use of Ref with a string representing the provisioned DynamoDB table. There is no way for a developer going through the create function to easily trace back which resource is referenced in the handler’s environment. It is also quite easy to accidentally change this value without noticing its impact. You will not get any feedback saying you referenced a non-existing resource until you actually deploy to AWS.

To make it more developer friendly, you can write a small service handling the AWS intrinsic function writing for you. This service actually use both your whole resources value as well as the AWS CloudFormation single resource itself, to generate the correct output Ref syntax:

You can then use a much more natural syntax in your function configuration to let everyone know which definition you’re actually using the name of. It is also much easier to know which functions actually depends on the specified AWS resource, since your IDE can actively tell you all usage of the variable representing the MyTable resource.

CONCLUSION

Other IaC frameworks, such as the AWS CDK, have started using configuration syntax which allows a more functional definition of infrastructure.

The use of JavaScript and TypeScript objects to power definitions of the Serverless framework opens up a new world of possibilities — with fewer text-defined options, fewer errors, and making it easier for developers to know the extent of usage of a specific infrastructure block.