├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin └── lambda-with-compression.js ├── cdk.json ├── img ├── cdk-output.png ├── curl-get-response.png ├── curl-post-response.png ├── gzip-d-result.png └── postman-compression-results.png ├── lambda ├── get-gzip │ ├── data.json │ └── index.mjs └── post-gzip │ └── index.mjs ├── package-lock.json ├── package.json └── stack ├── apigateway.js ├── functions.js ├── furls.js └── lambda-with-compression-stack.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # CDK asset staging directory 4 | .cdk.staging 5 | cdk.out 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # CDK asset staging directory 2 | .cdk.staging 3 | cdk.out 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using data compression with AWS Lambda functions 2 | 3 | This sample illustrates using data compression with API Gateway and Lambda functions to 4 | 1. Receive and return payloads larger than the maximum Lambda's payload size of 6MB 5 | 2. Potentially save on data transfer costs when using Lambda functions with NAT Gateway or VPC Endpoints 6 | 7 | While discussed in context of API Gateway and Lambda, the same compression technique can be applied to other serverless usecases as well, such as transporting larger payloads in SQS queue messages or EventBridge events. 8 | 9 | > Important: this sample uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are fully responsible for any AWS costs incurred. This sample is provided for educational purposes only. 10 | 11 | ## Overview 12 | 13 | The Lambda Invoke API supports requests and responses in uncompressed plain-text formats, such as JSON. The payload size for both request and response is limited to 6MB. 14 | 15 | While the Invoke API supports uncompressed text-based payloads, you can easily introduce data compression in your function code. In case you're invoking the function directly via the Invoke API or AWS SDK – you’ll need to handle compression and decompression on both server and client side. If you're invoking functions via API Gateway or Function URLs, these services can handle the compression automatically for you, as shown in this sample. 16 | 17 | Below results are obtained using Postman. 20MB of JSON is returned from Lambda function as a 2.5MB compressed payload. 18 | 19 | ![](img/postman-compression-results.png) 20 | 21 | ## Reducing payload sizes 22 | 23 | In this sample project, a 1MB JSON payload is received by and returned from a Lambda handler. The sample uses in-function compression, and illustrates how you can pass compressed payloads through Function URLs and API Gateway. You can use clients such as curl or Postman to test this functionality. This approach can help you to send and receive payloads larger than Lambda's limit of 6MB, as long as they're less than 6MB when compressed. 24 | 25 | ## Deployment Instructions 26 | 27 | ### Pre-reqs 28 | 29 | * [An AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) 30 | * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) 31 | * [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) 32 | * [AWS Cloud Development Kit](https://aws.amazon.com/cdk/) 33 | 34 | ### Installation steps 35 | 36 | 1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: 37 | ``` 38 | git clone https://github.com/aws-samples/lambda-with-compression 39 | ``` 40 | 1. Change directory to the pattern directory: 41 | ``` 42 | cd lambda-with-compression 43 | ``` 44 | 1. Install dependencies: 45 | ``` 46 | npm install 47 | ``` 48 | 1. Use AWS CDK to deploy the AWS resources for the pattern: 49 | ``` 50 | cdk deploy 51 | ``` 52 | 1. Allow CDK CLI to create IAM roles with the required permissions. 53 | 54 | 1. Note the output from the CDK deployment process. It contain function URLs and API Gateway andpoint you can use for testing. This sample stack enforces IAM authentication for Function URL for better security. You will either need to use your AWS credentials to invoke the generated Function URL, or change the authentication type to `FunctionUrlAuthType.NONE` in `stack/functions.js`. 55 | 56 | ![](img/cdk-output.png) 57 | 58 | ## Testing 59 | 60 | Use curl, Postman, or similar HTTP client to invoke Function URLs or API Gateway endpoints. 61 | 62 | #### Sending GET requests with curl 63 | 64 | The following command will retrieve 1MB of JSON compressed to approximately 125KB. You'll need to use either the `ApiEndpoint` or `GetGzipFurl` from the CDK output. 65 | 66 | ```bash 67 | curl $ApiGatewayEndpoint -v >output.json.gz 68 | ``` 69 | 70 | > When using Function URLs with IAM authorization enabled, you need to add authentication information with `--user $AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY 71 | --aws-sigv4 'aws:amz:{region}:lambda'` 72 | 73 | Note the `Content-Type`, `Content-Encoding`, and `Content-Length` headers 74 | 75 | ![](img/curl-get-response.png) 76 | 77 | Use `gzip` command to decompress received data 78 | 79 | ```bash 80 | gzip -dk output.json.gz 81 | ``` 82 | 83 | Note that the resulting output is 1MB in size, while received compressed payload is ~130KB. 84 | 85 | ![](img/gzip-d-result.png) 86 | 87 | You can use similar technique to POST compressed data to either API Gateway endpont or `PostGzipFurl` in a POST request. Note that you'll need to set `Content-Type=application/gzip` HTTP header. 88 | 89 | ```bash 90 | curl -X POST -v https://zem61fx3b1.execute-api.us-east-1.amazonaws.com/prod/ \ 91 | -H "Content-Type: application/gzip" \ 92 | --data-binary "@output.json.gz" 93 | ``` 94 | 95 | And see the response illustrating compression was used 96 | 97 | ![](img/curl-post-response.png) 98 | 99 | Note that ~130KB of gzip binary data becomes ~175KB when Base64 encoded. 100 | 101 | ## Reducing network footprint 102 | 103 | It is common for cloud solutions residing in a VPC to invoke Lambda functions. For example an EKS-based control plane needs to invoke a series of Lambda functions. This is usually achieved through either NAT Gateway or VPC Endpoint. 104 | 105 | Both NAT Gateway and VPC Endpoint are priced per GB of data processed, so reducing the volume of data by using compression can also reduce the data transfer cost. However, on the other hand, compressing/decompressing data is a CPU-intensive activity, which can require additional compute capacity and increase function invocation duration, and as a result function cost. Below results illustrate a series of tests ran to estimate the impact of data compression of Lambda function invocation duration, Lambda function invocation cost, and data transfer costs delta with both NAT Gateway and VPC Endpoint. 106 | 107 | > Below tests were performed with a series of assumptions and randomly generated JSON data. You should always perform your own performance/cost estimates with representative payloads. Different payloads will have different compression ratios. Payloads with low compression ratios might not benefit from this technique. 108 | 109 | ## Assumptions 110 | 111 | * Lambda cost (GB-s, ARM/Graviton2) - $0.000013 [(pricing)](https://aws.amazon.com/lambda/pricing/) 112 | * NAT Gateway cost (per GB) - $0.045 [(pricing)](https://aws.amazon.com/vpc/pricing/) 113 | * VPC Endpoint cost (per GB) - $0.010 [(pricing)](https://aws.amazon.com/privatelink/pricing/). 114 | * Tested with different randomly generated JSON payload sizes - 10KB, 100KB, 1MB, 5MB 115 | * Tested with different function memory configurations - 512MB, 1GB, 2GB (more allocated memory results in more allocated CPU capacity [(docs)](https://docs.aws.amazon.com/lambda/latest/dg/configuration-memory.html)). 116 | * Estimate cost deltas for processing 1,000,000 requests 117 | * Use gzip for data compression 118 | 119 | ## Testing results 120 | 121 | ### Added duration - invocation duration delta (ms) (compressed vs uncompressed) 122 | 123 | Compressing data is a CPU intensive activity, and as such it adds function invocation duration. The following chart illustrates the added duration in milliseconds when compressing JSON objects of various sizes with various memory allocations. E.g. compressing a 1MB JSON object with function configured with 1GB of allocated memory took on average 124ms. 124 | 125 | | | 512MB | 1GB | 2GB | 126 | | ----- | ----: | --: | --: | 127 | | 10KB | 1 | 1 | 1 | 128 | | 100KB | 27 | 13 | 9 | 129 | | 1MB | 251 | 124 | 70 | 130 | | 5MB | 1210 | 588 | 342 | 131 | 132 | ### Added cost - invocation duration delta cost (compressed vs uncompressed) 133 | 134 | The following chart illustrates the calculated estimate for added cost of compressing data. Continuing the example from previous chart - 1,000,000 invocations * 124ms of added duration per invocation * 1GB of allocated memory would result in 124,000 GB-seconds. 124,000 GB-seconds at $0.000013 per GB-second would cost $1.65. 135 | 136 | | | 512MB | 1GB | 2GB | 137 | | ----- | ----: | ----: | ----: | 138 | | 10KB | $0.01 | $0.01 | $0.03 | 139 | | 100KB | $0.18 | $0.17 | $0.24 | 140 | | 1MB | $1.67 | $1.65 | $1.87 | 141 | | 5MB | $8.07 | $7.84 | $8.64 | 142 | 143 | ### Savings - data transfer delta cost (compressed vs uncompressed) 144 | 145 | Compressed data is commonly represented as a binary stream/buffer. Lambda requires binary data to be encoded as base64 prior to returning via API Gateway / Function URLs. For brevety, the term "compression" below implies gzipping and subsequently base64-encoding the data. 146 | 147 | Mean compression ratio of randomly generated JSON objects was ~10-to-3, i.e. 1MB of JSON data produced a 300KB string after gzipping and base64-encoding, yielding ~70% space saving. 148 | 149 | The following chart illustrates estimated savings, or cost delta for sending uncompressed data vs compressed through NAT Gateway and VPC Endpoint. E.g. 150 | * Sending 1MB concompressed payload for 1,000,000 times would result in sending 1000GB total 151 | * Compressing the same 1MB payload to 300KB and sending it for 1,000,000 times would result in sending 300GB total 152 | * By compressing data you can reduce total network footprint by 700MB 153 | * When transported through NAT Gateway, 700GB * $0.045 per GB would cost $31.50 154 | 155 | | | NAT Gateway | VPC Endpoint (1 AZ) | 156 | | ----- | ----------: | -----------: | 157 | | 10KB | $0.32 | $0.07 | 158 | | 100KB | $3.15 | $0.70 | 159 | | 1MB | $31.50 | $7.00 | 160 | | 5MB | $157.50 | $35.00 | 161 | 162 | ### Conclusion 163 | 164 | In all test cases the savings from sending compressed data via NAT Gateway or VPC Endpoint were higher than the added cost of compressing data in Lambda. The data compression technique can be applied as an efficient cost savings mechanism if you can tolerate added latency, especially at a large scale of millions/billions of invocations. Additionally, the compression technique can be applied for sending and receiving payloads with sizes larger than the maximum payload limits. 165 | 166 | > Above tests were performed with a series of assumptions and randomly generated JSON data. You should ALWAYS perform your own performance/cost estimates with representative payloads. Different payloads will have different compression ratios. Payloads with low compression ratios might not benefit from this technique. 167 | 168 | ## Notes 169 | 170 | * Using alternative compression methods, such as [zstd](https://facebook.github.io/zstd/), may yield different results 171 | * For functions configured with >2GB of RAM it is important to use compression method that supports multiple vCPUs for maximum efficiency. 172 | * While this experiment focused on AWS Lambda, the same technique can be applied to any other service, e.g. reducing message sizes of large messages flowing through Amazon SQS. 173 | 174 | ## Cleanup 175 | 176 | Delete the stack 177 | 178 | ```bash 179 | cdk destroy 180 | ``` 181 | ---- 182 | Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 183 | 184 | SPDX-License-Identifier: MIT-0 -------------------------------------------------------------------------------- /bin/lambda-with-compression.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const cdk = require('aws-cdk-lib'); 4 | const { LambdaWithCompressionStack } = require('../stack/lambda-with-compression-stack'); 5 | 6 | const app = new cdk.App(); 7 | new LambdaWithCompressionStack(app, 'LambdaWithCompressionStack', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); 22 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "node bin/lambda-with-compression.js", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "jest.config.js", 11 | "package*.json", 12 | "yarn.lock", 13 | "node_modules", 14 | "test" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 19 | "@aws-cdk/core:checkSecretUsage": true, 20 | "@aws-cdk/core:target-partitions": [ 21 | "aws", 22 | "aws-cn" 23 | ], 24 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 25 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 26 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 27 | "@aws-cdk/aws-iam:minimizePolicies": true, 28 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 29 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 30 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 31 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 32 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 33 | "@aws-cdk/core:enablePartitionLiterals": true, 34 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 35 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 36 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 37 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 38 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 39 | "@aws-cdk/aws-route53-patters:useCertificate": true, 40 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 41 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 42 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, 43 | "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, 44 | "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, 45 | "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, 46 | "@aws-cdk/aws-redshift:columnId": true, 47 | "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, 48 | "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, 49 | "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, 50 | "@aws-cdk/aws-kms:aliasNameRef": true, 51 | "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, 52 | "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, 53 | "@aws-cdk/aws-efs:denyAnonymousAccess": true, 54 | "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, 55 | "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, 56 | "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, 57 | "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, 58 | "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, 59 | "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, 60 | "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /img/cdk-output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-with-compression/6a5f4fd0431ded665b5283d8cf28604af0c2775a/img/cdk-output.png -------------------------------------------------------------------------------- /img/curl-get-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-with-compression/6a5f4fd0431ded665b5283d8cf28604af0c2775a/img/curl-get-response.png -------------------------------------------------------------------------------- /img/curl-post-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-with-compression/6a5f4fd0431ded665b5283d8cf28604af0c2775a/img/curl-post-response.png -------------------------------------------------------------------------------- /img/gzip-d-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-with-compression/6a5f4fd0431ded665b5283d8cf28604af0c2775a/img/gzip-d-result.png -------------------------------------------------------------------------------- /img/postman-compression-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/lambda-with-compression/6a5f4fd0431ded665b5283d8cf28604af0c2775a/img/postman-compression-results.png -------------------------------------------------------------------------------- /lambda/get-gzip/index.mjs: -------------------------------------------------------------------------------- 1 | import { gzipSync } from 'zlib'; 2 | import {readFileSync} from 'fs'; 3 | 4 | export const handler = async (event) => { 5 | console.log(`> handler`); 6 | 7 | const DESIRED_PAYLOAD_SIZE_MB = 1; 8 | 9 | const filecontent = readFileSync('./data.json', 'utf-8'); 10 | const parsed = JSON.parse(filecontent); 11 | const bigarray = []; 12 | for (let i=0; i { 4 | console.log(`> handler isbase64Encoded=${event.isBase64Encoded}`); 5 | 6 | const responseBody = { 7 | wasRequestBase64Encoded: event.isBase64Encoded, 8 | }; 9 | 10 | const requestBody = event.body; 11 | responseBody.requestBodyLength = requestBody.length; 12 | 13 | if (event.isBase64Encoded){ 14 | const decoded = Buffer.from(requestBody, 'base64'); 15 | const decompressed = gunzipSync(decoded).toString('utf-8'); 16 | responseBody.decompressedBodyLength = decompressed.length; 17 | } 18 | 19 | return { 20 | statusCode: 200, 21 | body: JSON.stringify(responseBody), 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda-with-compression", 3 | "version": "0.1.0", 4 | "bin": { 5 | "lambda-with-compression": "bin/lambda-with-compression.js" 6 | }, 7 | "scripts": { 8 | "build": "echo \"The build step is not required when using JavaScript!\" && exit 0", 9 | "cdk": "cdk", 10 | "test": "jest" 11 | }, 12 | "devDependencies": { 13 | "aws-cdk": "2.122.0", 14 | "jest": "^29.7.0" 15 | }, 16 | "dependencies": { 17 | "@aws-sdk/client-dynamodb": "^3.521.0", 18 | "@aws-sdk/client-lambda": "^3.521.0", 19 | "aws-cdk-lib": "2.122.0", 20 | "constructs": "^10.0.0", 21 | "percentile": "^1.6.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /stack/apigateway.js: -------------------------------------------------------------------------------- 1 | const { Size } = require('aws-cdk-lib'); 2 | const { RestApi, LambdaIntegration } = require('aws-cdk-lib/aws-apigateway'); 3 | 4 | const bootstrap = (ctx, getGzipFn, postGzipFn) => { 5 | const api = new RestApi(ctx, 'CompressionDemoApi', { 6 | restApiName: 'compression-demo-api', 7 | 8 | // Enable compression for payloads >1KB 9 | minCompressionSize: Size.kibibytes(1), 10 | 11 | // Enable treating content-type=application/gzip as binary 12 | // This ensures that it is passed to the Lambda function 13 | // for further decompression 14 | binaryMediaTypes: ["application/gzip"], 15 | endpointExportName: "ApiGatewayEndpoint" 16 | }); 17 | 18 | api.root.addMethod('GET', new LambdaIntegration(getGzipFn, { 19 | proxy: true 20 | })); 21 | 22 | api.root.addMethod('POST', new LambdaIntegration(postGzipFn, { 23 | proxy: true 24 | })); 25 | } 26 | 27 | module.exports = { 28 | bootstrap 29 | }; -------------------------------------------------------------------------------- /stack/functions.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { Function, Runtime, Code } = require('aws-cdk-lib/aws-lambda'); 3 | const { Duration } = require('aws-cdk-lib'); 4 | 5 | const bootstrap = (ctx) => { 6 | const getGzipFn = new Function(ctx, 'GetGzipFn', { 7 | functionName: 'compression-demo-get-gzip', 8 | runtime: Runtime.NODEJS_20_X, 9 | handler: 'index.handler', 10 | memorySize: 1024, 11 | timeout: Duration.seconds(10), 12 | code: Code.fromAsset(path.join(__dirname, '../lambda/get-gzip')) 13 | }); 14 | 15 | const postGzipFn = new Function(ctx, 'PostGzipFn', { 16 | functionName: 'compression-demo-post-gzip', 17 | runtime: Runtime.NODEJS_20_X, 18 | handler: 'index.handler', 19 | memorySize: 1024, 20 | timeout: Duration.seconds(10), 21 | code: Code.fromAsset(path.join(__dirname, '../lambda/post-gzip')) 22 | }); 23 | 24 | return { 25 | getGzipFn, 26 | postGzipFn 27 | }; 28 | 29 | } 30 | 31 | module.exports = { 32 | bootstrap 33 | }; -------------------------------------------------------------------------------- /stack/furls.js: -------------------------------------------------------------------------------- 1 | const { FunctionUrlAuthType } = require('aws-cdk-lib/aws-lambda'); 2 | 3 | const bootstrap = (ctx, getGzipFn, postGzipFn) => { 4 | 5 | const getGzipFurl = getGzipFn.addFunctionUrl({ 6 | authType: FunctionUrlAuthType.AWS_IAM 7 | }); 8 | 9 | const postGzipFurl = postGzipFn.addFunctionUrl({ 10 | authType: FunctionUrlAuthType.AWS_IAM 11 | }); 12 | 13 | return { 14 | getGzipFurl, 15 | postGzipFurl 16 | }; 17 | 18 | } 19 | 20 | module.exports = { 21 | bootstrap 22 | }; -------------------------------------------------------------------------------- /stack/lambda-with-compression-stack.js: -------------------------------------------------------------------------------- 1 | const { Stack, CfnOutput } = require('aws-cdk-lib'); 2 | const functions = require('./functions'); 3 | const furls = require('./furls'); 4 | const apis = require('./apigateway'); 5 | 6 | class LambdaWithCompressionStack extends Stack { 7 | constructor(scope, id, props) { 8 | super(scope, id, props); 9 | 10 | const { 11 | getGzipFn, 12 | postGzipFn 13 | } = functions.bootstrap(this); 14 | 15 | const { 16 | getGzipFurl, 17 | postGzipFurl 18 | } = furls.bootstrap(this, getGzipFn, postGzipFn); 19 | 20 | apis.bootstrap(this, getGzipFn, postGzipFn); 21 | 22 | new CfnOutput(this, 'GetGzipFurl', { 23 | value: getGzipFurl.url 24 | }); 25 | 26 | new CfnOutput(this, 'PostGzipFurl', { 27 | value: postGzipFurl.url 28 | }); 29 | 30 | } 31 | } 32 | 33 | module.exports = { LambdaWithCompressionStack } 34 | --------------------------------------------------------------------------------