DEV Community

Cover image for Building Testable CloudFront Functions with TypeScript
Oleksandr Hanhaliuk
Oleksandr Hanhaliuk

Posted on

Building Testable CloudFront Functions with TypeScript

AWS CloudFront Functions are a powerful tool for running lightweight JavaScript code at the edge, allowing you to manipulate requests and responses.

However, AWS requires these functions to be written in Vanilla JavaScript, which can be limiting for developers who prefer TypeScript’s type safety and modern syntax.

In this article, I’ll walk you through a solution to write CloudFront Functions in TypeScript, import additional files, and test them effectively.

Challenge

CloudFront Functions must be written in ES5 JavaScript, which lacks the modern features and type safety of TypeScript. This requirement poses a challenge for developers who want to leverage TypeScript’s benefits while still deploying to CloudFront.

Solution

The solution involves using TypeScript to write your CloudFront Functions and then transpiling them to ES5 JavaScript. This approach allows you to maintain the benefits of TypeScript during development and testing, while still meeting AWS’s requirements for deployment.

Key Components

TypeScript Compiler Options:

  1. Configure the TypeScript compiler to target ES5 and remove module syntax, as CloudFront’s JavaScript environment doesn’t support all CommonJS runtime modules.
  2. Custom Transformers: Use custom TypeScript transformers to remove export keywords and __esModule properties, ensuring the output is compatible with CloudFront.
  3. Build Script: Create a build script to transpile TypeScript files to JavaScript, applying the custom transformers.
  4. Testing: Write unit tests for your TypeScript code using a testing framework like Jest, ensuring your logic is sound before deployment.

Implementation

Below is a simplified example of how you might set up your TypeScript project for CloudFront Functions:

TypeScript Configuration (tsconfig.json)

{
  "compilerOptions": {
    "target": "es5", // MUST BE ES5 for CloudFront Function support  https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-features.html#writing-functions-javascript-features-core
    "module": "commonjs", // Beware CloudFront JS environment doesn't contain all commonjs runtime modules    
    "lib": ["es5"],
    "strict": true,
    "removeComments": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
Enter fullscreen mode Exit fullscreen mode

Custom Transformer

Create a custom transformer to remove export keywords:

import * as ts from 'typescript';

export const removeExportTransformer: ts.TransformerFactory<ts.SourceFile> = (context) => {
  return (sourceFile) => {
    const visitor: ts.Visitor = (node) => {
      if (ts.isModifier(node) && node.kind === ts.SyntaxKind.ExportKeyword) {
        return undefined;
      }
      return ts.visitEachChild(node, visitor, context);
    };
    return ts.visitNode(sourceFile, visitor);
  };
};
Enter fullscreen mode Exit fullscreen mode

Build Script

A script to transpile TypeScript files:

import * as ts from 'typescript';
import * as fs from 'fs';
import * as path from 'path';
import { removeExportTransformer } from './removeExportTransformer';

const compilerOptions: ts.CompilerOptions = {
  module: ts.ModuleKind.None,
  target: ts.ScriptTarget.ES5,
  strict: true,
  removeComments: true,
  lib: ['es5'],
};

function transpileFile(filePath: string) {
  const source = fs.readFileSync(filePath, 'utf-8');
  const result = ts.transpileModule(source, {
    compilerOptions,
    transformers: { before: [removeExportTransformer] },
  });
  const outputFilePath = filePath.replace('.ts', '.js');
  fs.writeFileSync(outputFilePath, result.outputText);
}

const files = fs.readdirSync('./src').filter(file => file.endsWith('.ts'));
files.forEach(file => transpileFile(path.join('./src', file)));

Enter fullscreen mode Exit fullscreen mode

Usage

  1. Build your CloudFront Typescript function before deployment:
    ts-node scripts/build-cloudfront.ts

  2. Define the path to your function build output:

const function= new aws_cloudfront.Function(stack, 'CloudfrontFunctionId', {
  functionName: 'cloudfront_function',
  code: aws_cloudfront.FunctionCode.fromFile({
    filePath: `dist/cloudfrontFunction.js`,
  }),
  runtime: aws_cloudfront.FunctionRuntime.JS_2_0,
})
Enter fullscreen mode Exit fullscreen mode

Testing with Jest

Set up Jest to test your TypeScript code:

module.exports = {
  testEnvironment: 'node',
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
  },
  testMatch: ['**/__tests__/**/*.ts'],
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

By leveraging TypeScript and custom transformers, you can write, test, and deploy CloudFront Functions with modern JavaScript features and type safety benefits. This approach not only enhances your development experience but also ensures your code is robust and maintainable.

Full working code example you can find on my GitHub

Acknowledgements

Special thanks to the authors of the typescript-remove-exports package, which inspired the custom transformer approach used in this solution. Their work provided a foundation for adapting TypeScript code to meet CloudFront’s requirements.

Top comments (0)