├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DynamoDB ├── MoviesLoadData.js ├── create-MoviesTable.sh ├── load-MoviesTable.sh ├── moviedata-less.json ├── package-lock.json └── package.json ├── LICENSE ├── README.md ├── cdk ├── bin │ └── http-api-aws-lambda-container.ts ├── cdk.json ├── jest.config.js ├── lib │ └── http-api-aws-lambda-container-stack.ts ├── package.json └── tsconfig.json ├── images ├── API_Gateway.png ├── Architecture-1.png ├── Architecture.png ├── Cloud9.png ├── Cloud9Install.png ├── Docker-build.png ├── DynamoDB-Load.png ├── DynamoDB-create.png ├── DynamoDB-create_censored.jpg ├── GetFunction.png ├── GetFunction_censored.jpg ├── Lambda-1ms-Billing.png ├── cdk-deploy.png └── cdk-deploy_censored.jpg └── src └── movie-service ├── Dockerfile ├── get.js ├── list.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | 10 | # Parcel default cache directory 11 | .parcel-cache 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /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 *master* 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 | -------------------------------------------------------------------------------- /DynamoDB/MoviesLoadData.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5 | * 6 | * This file is licensed under the Apache License, Version 2.0 (the "License"). 7 | * You may not use this file except in compliance with the License. A copy of 8 | * the License is located at 9 | * 10 | * http://aws.amazon.com/apache2.0/ 11 | * 12 | * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 13 | * CONDITIONS OF ANY KIND, either express or implied. See the License for the 14 | * specific language governing permissions and limitations under the License. 15 | */ 16 | var AWS = require('aws-sdk'); 17 | var fs = require('fs'); 18 | 19 | if (!process.env.AWS_REGION) { 20 | console.error("Please specify the AWS region with the AWS_REGION environment variable"); 21 | process.exit(1); 22 | } 23 | 24 | AWS.config.update({ 25 | region: process.env.AWS_REGION, 26 | // endpoint: "http://localhost:8000" 27 | }); 28 | 29 | var docClient = new AWS.DynamoDB.DocumentClient(); 30 | 31 | console.log("Importing movies into DynamoDB. Please wait."); 32 | 33 | var allMovies = JSON.parse(fs.readFileSync('moviedata-less.json', 'utf8')); 34 | allMovies.forEach(function(movie) { 35 | var params = { 36 | TableName: "Movies", 37 | Item: { 38 | "year": movie.year, 39 | "title": movie.title, 40 | "info": movie.info 41 | } 42 | }; 43 | 44 | docClient.put(params, function(err, data) { 45 | if (err) { 46 | console.error("Unable to add movie", movie.title, ". Error JSON:", JSON.stringify(err, null, 2)); 47 | } else { 48 | console.log("PutItem succeeded:", movie.title); 49 | } 50 | }); 51 | }); -------------------------------------------------------------------------------- /DynamoDB/create-MoviesTable.sh: -------------------------------------------------------------------------------- 1 | aws dynamodb --region us-east-1 create-table \ 2 | --table-name Movies \ 3 | --attribute-definitions \ 4 | AttributeName=year,AttributeType=N \ 5 | AttributeName=title,AttributeType=S \ 6 | --key-schema \ 7 | AttributeName=year,KeyType=HASH \ 8 | AttributeName=title,KeyType=RANGE \ 9 | --provisioned-throughput \ 10 | ReadCapacityUnits=10,WriteCapacityUnits=5 11 | -------------------------------------------------------------------------------- /DynamoDB/load-MoviesTable.sh: -------------------------------------------------------------------------------- 1 | npm install 2 | export AWS_REGION=us-east-1 3 | node MoviesLoadData.js 4 | -------------------------------------------------------------------------------- /DynamoDB/moviedata-less.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "year": 2013, 4 | "title": "Rush", 5 | "info": { 6 | "directors": ["Ron Howard"], 7 | "release_date": "2013-09-02T00:00:00Z", 8 | "rating": 8.3, 9 | "genres": [ 10 | "Action", 11 | "Biography", 12 | "Drama", 13 | "Sport" 14 | ], 15 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg", 16 | "plot": "A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda.", 17 | "rank": 2, 18 | "running_time_secs": 7380, 19 | "actors": [ 20 | "Daniel Bruhl", 21 | "Chris Hemsworth", 22 | "Olivia Wilde" 23 | ] 24 | } 25 | }, 26 | { 27 | "year": 2013, 28 | "title": "Prisoners", 29 | "info": { 30 | "directors": ["Denis Villeneuve"], 31 | "release_date": "2013-08-30T00:00:00Z", 32 | "rating": 8.2, 33 | "genres": [ 34 | "Crime", 35 | "Drama", 36 | "Thriller" 37 | ], 38 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTg0NTIzMjQ1NV5BMl5BanBnXkFtZTcwNDc3MzM5OQ@@._V1_SX400_.jpg", 39 | "plot": "When Keller Dover's daughter and her friend go missing, he takes matters into his own hands as the police pursue multiple leads and the pressure mounts. But just how far will this desperate father go to protect his family?", 40 | "rank": 3, 41 | "running_time_secs": 9180, 42 | "actors": [ 43 | "Hugh Jackman", 44 | "Jake Gyllenhaal", 45 | "Viola Davis" 46 | ] 47 | } 48 | }, 49 | { 50 | "year": 2013, 51 | "title": "The Hunger Games: Catching Fire", 52 | "info": { 53 | "directors": ["Francis Lawrence"], 54 | "release_date": "2013-11-11T00:00:00Z", 55 | "genres": [ 56 | "Action", 57 | "Adventure", 58 | "Sci-Fi", 59 | "Thriller" 60 | ], 61 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTAyMjQ3OTAxMzNeQTJeQWpwZ15BbWU4MDU0NzA1MzAx._V1_SX400_.jpg", 62 | "plot": "Katniss Everdeen and Peeta Mellark become targets of the Capitol after their victory in the 74th Hunger Games sparks a rebellion in the Districts of Panem.", 63 | "rank": 4, 64 | "running_time_secs": 8760, 65 | "actors": [ 66 | "Jennifer Lawrence", 67 | "Josh Hutcherson", 68 | "Liam Hemsworth" 69 | ] 70 | } 71 | }, 72 | { 73 | "year": 2013, 74 | "title": "Thor: The Dark World", 75 | "info": { 76 | "directors": ["Alan Taylor"], 77 | "release_date": "2013-10-30T00:00:00Z", 78 | "genres": [ 79 | "Action", 80 | "Adventure", 81 | "Fantasy" 82 | ], 83 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQyNzAwOTUxOF5BMl5BanBnXkFtZTcwMTE0OTc5OQ@@._V1_SX400_.jpg", 84 | "plot": "Faced with an enemy that even Odin and Asgard cannot withstand, Thor must embark on his most perilous and personal journey yet, one that will reunite him with Jane Foster and force him to sacrifice everything to save us all.", 85 | "rank": 5, 86 | "actors": [ 87 | "Chris Hemsworth", 88 | "Natalie Portman", 89 | "Tom Hiddleston" 90 | ] 91 | } 92 | }, 93 | { 94 | "year": 2013, 95 | "title": "This Is the End", 96 | "info": { 97 | "directors": [ 98 | "Evan Goldberg", 99 | "Seth Rogen" 100 | ], 101 | "release_date": "2013-06-03T00:00:00Z", 102 | "rating": 7.2, 103 | "genres": [ 104 | "Comedy", 105 | "Fantasy" 106 | ], 107 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQxODE3NjM1Ml5BMl5BanBnXkFtZTcwMzkzNjc4OA@@._V1_SX400_.jpg", 108 | "plot": "While attending a party at James Franco's house, Seth Rogen, Jay Baruchel and many other celebrities are faced with the apocalypse.", 109 | "rank": 6, 110 | "running_time_secs": 6420, 111 | "actors": [ 112 | "James Franco", 113 | "Jonah Hill", 114 | "Seth Rogen" 115 | ] 116 | } 117 | }, 118 | { 119 | "year": 2013, 120 | "title": "Insidious: Chapter 2", 121 | "info": { 122 | "directors": ["James Wan"], 123 | "release_date": "2013-09-13T00:00:00Z", 124 | "rating": 7.1, 125 | "genres": [ 126 | "Horror", 127 | "Thriller" 128 | ], 129 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTg0OTA5ODIxNF5BMl5BanBnXkFtZTcwNTUzNDg4OQ@@._V1_SX400_.jpg", 130 | "plot": "The haunted Lambert family seeks to uncover the mysterious childhood secret that has left them dangerously connected to the spirit world.", 131 | "rank": 7, 132 | "running_time_secs": 6360, 133 | "actors": [ 134 | "Patrick Wilson", 135 | "Rose Byrne", 136 | "Barbara Hershey" 137 | ] 138 | } 139 | }, 140 | { 141 | "year": 2013, 142 | "title": "World War Z", 143 | "info": { 144 | "directors": ["Marc Forster"], 145 | "release_date": "2013-06-02T00:00:00Z", 146 | "rating": 7.1, 147 | "genres": [ 148 | "Action", 149 | "Adventure", 150 | "Horror", 151 | "Sci-Fi", 152 | "Thriller" 153 | ], 154 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTg0NTgxMjIxOF5BMl5BanBnXkFtZTcwMDM0MDY1OQ@@._V1_SX400_.jpg", 155 | "plot": "United Nations employee Gerry Lane traverses the world in a race against time to stop the Zombie pandemic that is toppling armies and governments, and threatening to destroy humanity itself.", 156 | "rank": 8, 157 | "running_time_secs": 6960, 158 | "actors": [ 159 | "Brad Pitt", 160 | "Mireille Enos", 161 | "Daniella Kertesz" 162 | ] 163 | } 164 | }, 165 | { 166 | "year": 2014, 167 | "title": "X-Men: Days of Future Past", 168 | "info": { 169 | "directors": ["Bryan Singer"], 170 | "release_date": "2014-05-21T00:00:00Z", 171 | "genres": [ 172 | "Action", 173 | "Adventure", 174 | "Fantasy", 175 | "Sci-Fi" 176 | ], 177 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQ0NzIwNTA1MV5BMl5BanBnXkFtZTgwNjY2OTcwMDE@._V1_SX400_.jpg", 178 | "plot": "The X-Men send Wolverine to the past to change a major historical event that could globally impact man and mutant kind.", 179 | "rank": 9, 180 | "actors": [ 181 | "Jennifer Lawrence", 182 | "Hugh Jackman", 183 | "Michael Fassbender" 184 | ] 185 | } 186 | }, 187 | { 188 | "year": 2014, 189 | "title": "Transformers: Age of Extinction", 190 | "info": { 191 | "directors": ["Michael Bay"], 192 | "release_date": "2014-06-25T00:00:00Z", 193 | "genres": [ 194 | "Action", 195 | "Adventure", 196 | "Sci-Fi" 197 | ], 198 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTQyMDA5Nzg0Nl5BMl5BanBnXkFtZTgwNzA4NDcxMDE@._V1_SX400_.jpg", 199 | "plot": "A mechanic and his daughter make a discovery that brings down Autobots and Decepticons - and a paranoid government official - on them.", 200 | "rank": 10, 201 | "actors": [ 202 | "Mark Wahlberg", 203 | "Nicola Peltz", 204 | "Jack Reynor" 205 | ] 206 | } 207 | }, 208 | { 209 | "year": 2013, 210 | "title": "Now You See Me", 211 | "info": { 212 | "directors": ["Louis Leterrier"], 213 | "release_date": "2013-05-21T00:00:00Z", 214 | "rating": 7.3, 215 | "genres": [ 216 | "Crime", 217 | "Mystery", 218 | "Thriller" 219 | ], 220 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTY0NDY3MDMxN15BMl5BanBnXkFtZTcwOTM5NzMzOQ@@._V1_SX400_.jpg", 221 | "plot": "An FBI agent and an Interpol detective track a team of illusionists who pull off bank heists during their performances and reward their audiences with the money.", 222 | "rank": 11, 223 | "running_time_secs": 6900, 224 | "actors": [ 225 | "Jesse Eisenberg", 226 | "Common", 227 | "Mark Ruffalo" 228 | ] 229 | } 230 | }, 231 | { 232 | "year": 2013, 233 | "title": "Gravity", 234 | "info": { 235 | "directors": ["Alfonso Cuaron"], 236 | "release_date": "2013-08-28T00:00:00Z", 237 | "rating": 8.2, 238 | "genres": [ 239 | "Drama", 240 | "Sci-Fi", 241 | "Thriller" 242 | ], 243 | "image_url": "http://ia.media-imdb.com/images/M/MV5BNjE5MzYwMzYxMF5BMl5BanBnXkFtZTcwOTk4MTk0OQ@@._V1_SX400_.jpg", 244 | "plot": "A medical engineer and an astronaut work together to survive after an accident leaves them adrift in space.", 245 | "rank": 12, 246 | "running_time_secs": 5400, 247 | "actors": [ 248 | "Sandra Bullock", 249 | "George Clooney", 250 | "Ed Harris" 251 | ] 252 | } 253 | }, 254 | { 255 | "year": 2013, 256 | "title": "We're the Millers", 257 | "info": { 258 | "directors": ["Rawson Marshall Thurber"], 259 | "release_date": "2013-08-03T00:00:00Z", 260 | "rating": 7.2, 261 | "genres": [ 262 | "Comedy", 263 | "Crime" 264 | ], 265 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMjA5Njc0NDUxNV5BMl5BanBnXkFtZTcwMjYzNzU1OQ@@._V1_SX400_.jpg", 266 | "plot": "A veteran pot dealer creates a fake family as part of his plan to move a huge shipment of weed into the U.S. from Mexico.", 267 | "rank": 13, 268 | "running_time_secs": 6600, 269 | "actors": [ 270 | "Jason Sudeikis", 271 | "Jennifer Aniston", 272 | "Emma Roberts" 273 | ] 274 | } 275 | }, 276 | { 277 | "year": 2013, 278 | "title": "Riddick", 279 | "info": { 280 | "directors": ["David Twohy"], 281 | "release_date": "2013-09-04T00:00:00Z", 282 | "rating": 6.8, 283 | "genres": [ 284 | "Action", 285 | "Sci-Fi", 286 | "Thriller" 287 | ], 288 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTk5NzYwMzQ4MV5BMl5BanBnXkFtZTcwMjE5MTI1OQ@@._V1_SX400_.jpg", 289 | "plot": "Left for dead on a sun-scorched planet, Riddick finds himself up against an alien race of predators. Activating an emergency beacon alerts two ships: one carrying a new breed of mercenary, the other captained by a man from Riddick's past.", 290 | "rank": 14, 291 | "running_time_secs": 7140, 292 | "actors": [ 293 | "Vin Diesel", 294 | "Karl Urban", 295 | "Katee Sackhoff" 296 | ] 297 | } 298 | }, 299 | { 300 | "year": 2013, 301 | "title": "The Family", 302 | "info": { 303 | "directors": ["Luc Besson"], 304 | "release_date": "2013-09-10T00:00:00Z", 305 | "rating": 6.5, 306 | "genres": [ 307 | "Action", 308 | "Comedy", 309 | "Crime" 310 | ], 311 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMjE2MzI0MzkyNV5BMl5BanBnXkFtZTcwMjQ2MDM2OQ@@._V1_SX400_.jpg", 312 | "plot": "The Manzoni family, a notorious mafia clan, is relocated to Normandy, France under the witness protection program, where fitting in soon becomes challenging as their old habits die hard.", 313 | "rank": 15, 314 | "running_time_secs": 6660, 315 | "actors": [ 316 | "Robert De Niro", 317 | "Michelle Pfeiffer", 318 | "Dianna Agron" 319 | ] 320 | } 321 | }, 322 | { 323 | "year": 2013, 324 | "title": "Star Trek Into Darkness", 325 | "info": { 326 | "directors": ["J.J. Abrams"], 327 | "release_date": "2013-05-02T00:00:00Z", 328 | "rating": 7.9, 329 | "genres": [ 330 | "Action", 331 | "Adventure", 332 | "Sci-Fi" 333 | ], 334 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTk2NzczOTgxNF5BMl5BanBnXkFtZTcwODQ5ODczOQ@@._V1_SX400_.jpg", 335 | "plot": "After the crew of the Enterprise find an unstoppable force of terror from within their own organization, Captain Kirk leads a manhunt to a war-zone world to capture a one man weapon of mass destruction.", 336 | "rank": 16, 337 | "running_time_secs": 7920, 338 | "actors": [ 339 | "Chris Pine", 340 | "Zachary Quinto", 341 | "Zoe Saldana" 342 | ] 343 | } 344 | }, 345 | { 346 | "year": 2013, 347 | "title": "After Earth", 348 | "info": { 349 | "directors": ["M. Night Shyamalan"], 350 | "release_date": "2013-05-01T00:00:00Z", 351 | "rating": 4.9, 352 | "genres": [ 353 | "Action", 354 | "Adventure", 355 | "Sci-Fi" 356 | ], 357 | "image_url": "http://ia.media-imdb.com/images/M/MV5BMTY3MzQyMjkwMl5BMl5BanBnXkFtZTcwMDk2OTE0OQ@@._V1_SX400_.jpg", 358 | "plot": "A crash landing leaves Kitai Raige and his father Cypher stranded on Earth, a millennium after events forced humanity's escape. With Cypher injured, Kitai must embark on a perilous journey to signal for help.", 359 | "rank": 17, 360 | "running_time_secs": 6000, 361 | "actors": [ 362 | "Jaden Smith", 363 | "David Denman", 364 | "Will Smith" 365 | ] 366 | } 367 | } 368 | ] -------------------------------------------------------------------------------- /DynamoDB/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "environment", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "aws-sdk": { 8 | "version": "2.814.0", 9 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.814.0.tgz", 10 | "integrity": "sha512-empd1m/J/MAkL6d9OeRpmg9thobULu0wk4v8W3JToaxGi2TD7PIdvE6yliZKyOVAdJINhBWEBhxR4OUIHhcGbQ==", 11 | "requires": { 12 | "buffer": "4.9.2", 13 | "events": "1.1.1", 14 | "ieee754": "1.1.13", 15 | "jmespath": "0.15.0", 16 | "querystring": "0.2.0", 17 | "sax": "1.2.1", 18 | "url": "0.10.3", 19 | "uuid": "3.3.2", 20 | "xml2js": "0.4.19" 21 | } 22 | }, 23 | "base64-js": { 24 | "version": "1.5.1", 25 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 26 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" 27 | }, 28 | "buffer": { 29 | "version": "4.9.2", 30 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", 31 | "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", 32 | "requires": { 33 | "base64-js": "^1.0.2", 34 | "ieee754": "^1.1.4", 35 | "isarray": "^1.0.0" 36 | } 37 | }, 38 | "events": { 39 | "version": "1.1.1", 40 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 41 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 42 | }, 43 | "ieee754": { 44 | "version": "1.1.13", 45 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 46 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 47 | }, 48 | "isarray": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 51 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 52 | }, 53 | "jmespath": { 54 | "version": "0.15.0", 55 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 56 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 57 | }, 58 | "punycode": { 59 | "version": "1.3.2", 60 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 61 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 62 | }, 63 | "querystring": { 64 | "version": "0.2.0", 65 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 66 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 67 | }, 68 | "sax": { 69 | "version": "1.2.1", 70 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 71 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 72 | }, 73 | "url": { 74 | "version": "0.10.3", 75 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 76 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 77 | "requires": { 78 | "punycode": "1.3.2", 79 | "querystring": "0.2.0" 80 | } 81 | }, 82 | "uuid": { 83 | "version": "3.3.2", 84 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 85 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 86 | }, 87 | "xml2js": { 88 | "version": "0.4.19", 89 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 90 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 91 | "requires": { 92 | "sax": ">=0.6.0", 93 | "xmlbuilder": "~9.0.1" 94 | } 95 | }, 96 | "xmlbuilder": { 97 | "version": "9.0.7", 98 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 99 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /DynamoDB/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "environment", 3 | "version": "1.0.0", 4 | "description": "___ ______ ____ _ _ ___ / \\ \\ / / ___| / ___| | ___ _ _ __| |/ _ \\ / _ \\ \\ /\\ / /\\___ \\ | | | |/ _ \\| | | |/ _` | (_) | / ___ \\ V V / ___) | | |___| | (_) | |_| | (_| |\\__, | /_/ \\_\\_/\\_/ |____/ \\____|_|\\___/ \\__,_|\\__,_| /_/ -----------------------------------------------------------------", 5 | "main": "MoviesLoadData.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "aws-sdk": "^2.814.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building APIs using container image support for AWS Lambda 2 | ![Build Status](https://codebuild.us-east-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiSy9rWmVENzRDbXBoVlhYaHBsNks4OGJDRXFtV1IySmhCVjJoaytDU2dtVWhhVys3NS9Odk5DbC9lR2JUTkRvSWlHSXZrNVhYQ3ZsaUJFY3o4OERQY1pnPSIsIml2UGFyYW1ldGVyU3BlYyI6IlB3ODEyRW9KdU0yaEp6NDkiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=master) 3 | [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/aws/aws-cdk) 4 | [![NPM version](https://badge.fury.io/js/aws-cdk.svg)](https://badge.fury.io/js/aws-cdk) 5 | [![PyPI version](https://badge.fury.io/py/aws-cdk.core.svg)](https://badge.fury.io/py/aws-cdk.core) 6 | [![NuGet version](https://badge.fury.io/nu/Amazon.CDK.svg)](https://badge.fury.io/nu/Amazon.CDK) 7 | 8 | At AWS re:Invent 2020, [AWS Lambda](https://aws.amazon.com/lambda) released [Container Image Support for Lambda functions](https://aws.amazon.com/blogs/aws/new-for-aws-lambda-container-image-support/). With this new feature, AWS Lambda now enables you to package and deploy functions as container images. Customers can leverage the flexibility and familiarity of container tooling, and the agility and operational simplicity of AWS Lambda to build applications. 9 | - Many customers have invested in container tooling, development workflows, and expertise. 10 | - Customers using container tooling and packaging couldn’t get the full benefits of AWS Lambda. 11 | - Customers couldn’t use their preferred community or private enterprise container images. 12 | 13 | This project discusses the architecture and implementation of an HTTP API that is backed by two AWS Lambda functions packaged as a container image. These Lambda functions use the AWS SDK to retrieve data from a backend Amazon DynamoDB table. We use [AWS CDK](https://aws.amazon.com/cdk/) for the implementation of this architecture. 14 | 15 | We will also discuss the new AWS Lambda 1ms Billing Granularity that adds to the cost savings for customers. 16 | 17 | ## Contributors 18 | 1. Irshad A Buchh, Amazon Web Services 19 | 2. Carl Zogheib, Software Development Engineer (AWS Lambda), Amazon Web Services 20 | 21 | ## Prerequisites 22 | In order to implement the instructions laid out in this post, you will need the following: 23 | 24 | - An [AWS account](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/) 25 | - A [GitHub account](https://help.github.com/en/github/getting-started-with-github/signing-up-for-a-new-github-account) 26 | 27 | ## Architecture 28 | architecture-screenshot 29 | 30 | Here are the steps we’ll be following to implement the above architecture: 31 | 32 | - Create and configure AWS Cloud9 environment 33 | - Create Amazon DynamoDB Movies Table 34 | - Load Sample data into Movies Table 35 | - Create Dockerfile 36 | - Create Lambda functions 37 | - Build Docker image 38 | - Test Lambda Functions locally 39 | - Deploy Lambda functions using container image support 40 | - Provision AWS resources using the AWS CDK 41 | - Test the HTTP API 42 | - Cleanup 43 | - Conclusion 44 | 45 | ## Create and configure AWS Cloud9 environment 46 | Developers can use their local machines to set up an environment and using AWS Cloud9 is an option. However in this blog post we shall use AWS CLoud9 for development. Log into the AWS Management Console and search for [Cloud9](https://aws.amazon.com/cloud9/) service in the search bar. 47 | 48 | Cloud9 49 | 50 | 1. Select Cloud9 and create an AWS Cloud9 environment based on Amazon Linux 2. 51 | - We will be using the us-east-1 region for this example, so our Cloud9 environment will be created there. 52 | 2. Create an IAM role for Cloud9 workspace as explained [here](https://www.eksworkshop.com/020_prerequisites/iamrole/). 53 | 3. Attach the IAM role to your workspace as explained [here](https://www.eksworkshop.com/020_prerequisites/ec2instance/). 54 | 4. Turn off the AWS managed temporary credentials of the Cloud9 environment as explained [here](https://www.eksworkshop.com/020_prerequisites/workspaceiam/). 55 | - You can also resize the Amazon Elastic Block Store (Amazon EBS) volume that is associated with an Amazon EC2 instance for an environment. The detailed steps are documented [here](https://docs.aws.amazon.com/cloud9/latest/user-guide/move-environment.html#move-environment-resize). 56 | 5. Open a new terminal in Cloud9. 57 | 6. Install jq by running: 58 | ```bash 59 | sudo yum install jq -y 60 | ``` 61 | 7. Clone the GitHub repository containing the code sample for this example: 62 | ```bash 63 | git clone https://github.com/aws-samples/aws-cdk-lambda-container.git 64 | ``` 65 | 66 | ## Create Amazon DynamoDB Movies Table 67 | 68 | We shall be using the example Movies table as explained in the [AWS Documentation on creating a DynamoDB Table with the AWS SDK for JavaScript](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.NodeJs.01.html). 69 | 70 | Let us create a Movies table with a composite Primary Key comprising: 71 | 72 | - Partition Key --- year 73 | - Sort Key --- title 74 | 75 | ```bash 76 | aws dynamodb --region us-east-1 create-table \ 77 | --table-name Movies \ 78 | --attribute-definitions \ 79 | AttributeName=year,AttributeType=N \ 80 | AttributeName=title,AttributeType=S \ 81 | --key-schema \ 82 | AttributeName=year,KeyType=HASH \ 83 | AttributeName=title,KeyType=RANGE \ 84 | --provisioned-throughput \ 85 | ReadCapacityUnits=10,WriteCapacityUnits=5 86 | ``` 87 | This will return the Movies table details as: 88 | 89 | 90 | 91 | To verify that DynamoDB has finished creating the Movies table, use the describe-table command: 92 | 93 | ```bash 94 | aws dynamodb --region us-east-1 describe-table --table-name Movies | grep TableStatus 95 | ``` 96 | Proceed to the next step if you get "TableStatus": "ACTIVE". 97 | Otherwise if your table is marked as “CREATING”, wait a few seconds and try again. 98 | 99 | ## Load Sample data into Movies Table 100 | Navigate to the DynamoDB directory in the code sample and run the MoviesLoadData.js NodeJS script. 101 | 102 | ```bash 103 | cd ~/environment/aws-cdk-lambda-container/DynamoDB 104 | npm install 105 | export AWS_REGION=us-east-1 106 | node MoviesLoadData.js 107 | ``` 108 | This script will load sample movie data into the newly created **Movies** DynamoDB table. 109 | 110 | 111 | 112 | ## Query Data 113 | Run the following command to make sure that you can query the movie data of the 2013 movie Rush. 114 | 115 | ```bash 116 | aws dynamodb --region us-east-1 \ 117 | get-item --consistent-read \ 118 | --table-name Movies \ 119 | --key '{ "year": {"N": "2013"}, "title": {"S": "Rush"}}' 120 | ``` 121 | 122 | ## Create Lambda functions 123 | We shall write a couple of Lambda functions list.js and get.js. 124 | ### list.js function: 125 | 126 | The function retrieves all movies in the Movies table and the code is located here: ~/environment/aws-cdk-lambda-container/src/movie-service/list.js 127 | 128 | ```javascript 129 | 'use strict'; 130 | 131 | const AWS = require('aws-sdk'); 132 | const log = require('lambda-log'); 133 | 134 | const dynamoDb = new AWS.DynamoDB(); 135 | const params = { 136 | TableName: process.env.DYNAMODB_TABLE, 137 | }; 138 | 139 | module.exports.list = (event, context, callback) => { 140 | log.options.debug = true; 141 | log.debug(params); 142 | 143 | // fetch all Movies from the database 144 | 145 | dynamoDb.scan(params, (error, result) => { 146 | 147 | // handle potential errors 148 | if (error) { 149 | console.error(error); 150 | callback(null, { 151 | statusCode: error.statusCode || 501, 152 | headers: { 'Content-Type': 'text/plain' }, 153 | body: 'Couldn\'t fetch the Movies.', 154 | }); 155 | return; 156 | } 157 | 158 | // create a response 159 | const response = { 160 | statusCode: 200, 161 | body: JSON.stringify(result.Items), 162 | }; 163 | log.debug(response); 164 | callback(null, response); 165 | }); 166 | }; 167 | ``` 168 | We are also using the universal JSON logger NPM package lambda-log. 169 | 170 | ### get.js function: 171 | 172 | The function retrieves a movie item from the Movies table based on two input parameters, the year and title of the movie. These parameters are later passed into this function through an HTTP API via API Gateway. The get.js function code is located here: ~/environment/aws-cdk-lambda-container/src/movie-service/get.js 173 | 174 | ```javascript 175 | 'use strict'; 176 | 177 | const AWS = require('aws-sdk'); 178 | const log = require('lambda-log'); 179 | 180 | const dynamoDb = new AWS.DynamoDB(); 181 | 182 | module.exports.get = (event, context, callback) => { 183 | var params = { 184 | TableName: process.env.DYNAMODB_TABLE, 185 | Key: { 186 | "year": { "N": event.pathParameters.year }, 187 | "title": { "S": event.pathParameters.title.toString() } 188 | } 189 | }; 190 | 191 | log.options.debug = true; 192 | log.debug(params); 193 | 194 | // fetch Movie from the database 195 | dynamoDb.getItem(params, (error, result) => { 196 | // handle potential errors 197 | if (error) { 198 | console.error(error); 199 | callback(null, { 200 | statusCode: error.statusCode || 501, 201 | headers: { 'Content-Type': 'text/plain' }, 202 | body: 'Couldn\'t fetch the Movie.', 203 | }); 204 | return; 205 | } 206 | 207 | // create a response 208 | const response = { 209 | statusCode: 200, 210 | body: JSON.stringify(result.Item), 211 | }; 212 | log.debug(response); 213 | callback(null, response); 214 | }); 215 | }; 216 | ``` 217 | ## Create Dockerfile 218 | A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble a container image. You’ll find a Dockerfile in your current workspace under: 219 | ~/environment/aws-cdk-lambda-container/src/movie-service/Dockerfile 220 | 221 | ```docker 222 | FROM public.ecr.aws/lambda/nodejs:12 223 | # Alternatively, you can pull the base image from Docker Hub: amazon/aws-lambda-nodejs:12 224 | 225 | # Copy the Lambda functions 226 | COPY list.js get.js package.json package-lock.json ${LAMBDA_TASK_ROOT}/ 227 | 228 | # Install NPM dependencies for function 229 | RUN npm install 230 | ``` 231 | This Dockerfile specifies the publicly available AWS base image for Lambda with NodeJS 12 public.ecr.aws/lambda/nodejs:12. It copies the list.js, get.js, package.json and package-lock.json files into the ${LAMBDA_TASK_ROOT} folder, then runs npm install to fetch the function’s dependencies. The ${LAMBDA_TASK_ROOT} represents the path to our Lambda functions as documented in the [AWS Documentation on using AWS Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html). 232 | 233 | ## Build Docker image 234 | 235 | Now that we have written the Dockerfile and the two lambda functions, let’s take a look at [building](https://docs.docker.com/engine/reference/commandline/build/) our Docker container image. A container image includes everything you need to run an application - the code or binary, runtime, dependencies, and any other file system objects required. 236 | From the Cloud9 terminal run the following commands: 237 | 238 | ``` 239 | cd ~/environment/aws-cdk-lambda-container/src/movie-service 240 | docker build -t movie-service . 241 | docker images | grep movie-service 242 | ``` 243 | 244 | 245 | 246 | 247 | ## Test Lambda Functions locally 248 | In order to locally test our Lambda functions packaged as a container image, we shall use [AWS Lambda Runtime Interface Emulator (RIE)](https://github.com/aws/aws-lambda-runtime-interface-emulator) which is a proxy for the Lambda Runtime API. 249 | The Lambda Runtime Interface Emulator (RIE) is a lightweight web server that converts HTTP requests into JSON events to pass to the Lambda functions in the container image. 250 | In the container image, we need to configure the following environment variables: 251 | - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, and AWS_REGION for authentication with the AWS SDK. We are going to use the AWS CLI to get our current environment’s credentials and pass those along to our local container through the use of [aws configure get](https://docs.aws.amazon.com/cli/latest/reference/configure/get.html). 252 | - DYNAMODB_TABLE to point our **list** and **get** functions to our newly-created dataset in the **Movies** table. 253 | 254 | **Run list.js function:** From the Cloud9 terminal run the following command. This command runs the movie-service image as a container and starts up an endpoint for list.js function locally at : http://localhost:9080/2015-03-31/functions/function/invocations 255 | 256 | ```bash 257 | docker run \ 258 | --env DYNAMODB_TABLE=Movies \ 259 | --env AWS_ACCESS_KEY_ID="$(aws configure get default.aws_access_key_id)" \ 260 | --env AWS_SECRET_ACCESS_KEY="$(aws configure get default.aws_secret_access_key)" \ 261 | --env AWS_SESSION_TOKEN="$(aws configure get default.aws_session_token)" \ 262 | --env AWS_REGION="$(aws configure get default.region)" \ 263 | -p 9080:8080 \ 264 | movie-service list.list 265 | 266 | ``` 267 | **Test list.js function:** Open a new Cloud9 terminal and run the following command. This command invokes list.js function. 268 | ```bash 269 | curl -s "http://localhost:9080/2015-03-31/functions/function/invocations" -d '{}' | jq 270 | ``` 271 | 272 | **Run get.js function:** From the Cloud9 terminal run the following command. The following command runs the movie-service image as a container and starts up an endpoint for get.js function locally at: http://localhost:9080/2015-03-31/functions/function/invocations 273 | 274 | ```bash 275 | docker run \ 276 | --env DYNAMODB_TABLE=Movies \ 277 | --env AWS_ACCESS_KEY_ID="$(aws configure get default.aws_access_key_id)" \ 278 | --env AWS_SECRET_ACCESS_KEY="$(aws configure get default.aws_secret_access_key)" \ 279 | --env AWS_SESSION_TOKEN="$(aws configure get default.aws_session_token)" \ 280 | --env AWS_REGION="$(aws configure get default.region)" \ 281 | -p 9080:8080 \ 282 | movie-service get.get 283 | ``` 284 | 285 | **Test get.js function:** Open a new Cloud9 terminal and run the following command. This command invokes **get.js** function with two variables **year="2013"** and **title=”Rush”** under the key labelled **“pathParameters”** to simulate an incoming API Gateway request. 286 | ```bash 287 | curl -s "http://localhost:9080/2015-03-31/functions/function/invocations" -d '{"pathParameters": {"year": "2013", "title": "Rush"} }' | jq 288 | ``` 289 | 290 | ## Deploy Lambda Functions using container image support 291 | ### Install AWS CDK 292 | 293 | The [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/) is an open-source software development framework to model and provision your cloud application resources using familiar programming languages. If you would like to familiarize yourself the CDKWorkshop is a great place to start. 294 | Using Cloud9 environment, open a new terminal and use the following commands: 295 | ```bash 296 | cd ~/environment/aws-cdk-lambda-container/cdk 297 | npm install 298 | ``` 299 | 300 | This will install all the latest CDK modules under the *node_modules* directory. 301 | 302 | ## Creating AWS resources using the CDK 303 | We shall implement this architecture using an AWS CDK application consisting of one CDK stack written in typescript. Under the *cdk/lib* folder, open the *http-api-aws-lambda-container-stack.ts* file and let us explore the following different CDK constructs. 304 | 305 | ### DynamoDB table 306 | Since we have created the Movies DynamoDB table earlier using the AWS CLI, we can import this existing table using AWS CDK as documented [here](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-dynamodb.ITable.html). 307 | 308 | ```typescript 309 | const table = dynamodb.Table.fromTableName(this, 'MoviesTable', 'Movies'); 310 | ``` 311 | 312 | ### Lambda functions 313 | Developers can now package and deploy AWS Lambda functions as a container image of up to 10 GB. This makes it easy to build Lambda based applications using familiar container tooling, workflows, and dependencies. Let us create two Lambda functions using the AWS CDK [DockerImageFunction](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.DockerImageFunction.html) class. The code attribute is using [static fromImageAsset(directory, props?)](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.DockerImageCode.html#static-fromwbrimagewbrassetdirectory-propsspan-classapi-icon-api-icon-experimental-titlethis-api-element-is-experimental-it-may-change-without-noticespan) method of the [DockerImageCode](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.DockerImageCode.html) class and it picks up the Dockerfile under src/movie-service directory. 314 | 315 | **listMovieFunction:** 316 | 317 | ```typescript 318 | const listMovieFunction = new lambda.DockerImageFunction(this, 'listMovieFunction',{ 319 | functionName: 'listMovieFunction', 320 | code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), { 321 | cmd: [ "list.list" ], 322 | entrypoint: ["/lambda-entrypoint.sh"], 323 | }), 324 | environment: { 325 | DYNAMODB_TABLE: this.table.tableName 326 | }, 327 | }); 328 | ``` 329 | **getMovieFunction:** 330 | 331 | ```typescript 332 | const getMovieFunction = new lambda.DockerImageFunction(this, 'getMovieFunction',{ 333 | functionName: 'getMovieFunction', 334 | code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), { 335 | cmd: [ "get.get" ], 336 | entrypoint: ["/lambda-entrypoint.sh"], 337 | }), 338 | environment: { 339 | DYNAMODB_TABLE: this.table.tableName 340 | }, 341 | }); 342 | ``` 343 | ### Lambda proxy integrations 344 | Amazon API Gateway Lambda proxy integration is a simple, powerful, and nimble mechanism to build an API with a setup of a single API method. The Lambda proxy integration allows the client to call a single Lambda function in the backend. In Lambda proxy integration, when a client submits an API request, API Gateway passes to the integrated Lambda function the raw request as-is. Let us create two Lambda proxy integrations for the two Lambda functions using LambdaProxyIntegration class which takes LambdaProxyIntegrationProps as an argument. 345 | 346 | **listMovieFunctionIntegration:** 347 | 348 | ```typescript 349 | const listMovieFunctionIntegration = new apigintegration.LambdaProxyIntegration({ 350 | handler: listMovieFunction, 351 | }); 352 | ``` 353 | **getMovieFunctionIntegration:** 354 | 355 | ```typescript 356 | const getMovieFunctionIntegration = new apigintegration.LambdaProxyIntegration({ 357 | handler: getMovieFunction, 358 | }); 359 | ``` 360 | 361 | ### HTTP API 362 | [HTTP APIs for Amazon API Gateway](https://aws.amazon.com/blogs/compute/announcing-http-apis-for-amazon-api-gateway/) enable developers to create RESTful APIs with lower latency and lower cost than REST APIs(for more information about HTTP APIs please check out [this AWS Compute Blog post](https://aws.amazon.com/blogs/compute/building-better-apis-http-apis-now-generally-available/)). We can use HTTP APIs to send requests to AWS Lambda functions. We shall create an HTTP API that integrates with the two Lambda functions on the backend. When a client calls this API, API Gateway sends the request to the Lambda function and returns the function's response back to the client. Here is the code for the [HTTP API](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigatewayv2.HttpApi.html) with a default stage. 363 | 364 | ```typescript 365 | const httpApi = new apig.HttpApi(this, "httpApi", { 366 | apiName: "httpApi", 367 | createDefaultStage: true, 368 | }); 369 | ``` 370 | 371 | ### HTTP API Routes 372 | HTTP API Routes consist of two parts: an HTTP method and a resource path. Routes direct incoming API requests to backend resources like AWS Lambda functions. We shall add a *GET /list* route to integrate with the listMovieFunction Lambda function and a *GET /{year}/{title}* route to integrate with the getMovieFunction Lambda function. For additional details, please refer to the HttpRoute class documented [here](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigatewayv2.HttpRoute.html). 373 | 374 | ```typescript 375 | httpApi.addRoutes({ 376 | integration: listMovieFunctionIntegration, 377 | methods: [apig.HttpMethod.GET], 378 | path: '/list', 379 | }); 380 | 381 | httpApi.addRoutes({ 382 | integration: getMovieFunctionIntegration, 383 | methods: [apig.HttpMethod.GET], 384 | path: '/get/{year}/{title}', 385 | }); 386 | ``` 387 | 388 | ## Provision AWS resources using the AWS CDK 389 | Using Cloud9 environment, open a new terminal and use the following commands: 390 | ```bash 391 | cd ~/environment/aws-cdk-lambda-container/cdk 392 | ``` 393 | Compile the Typescript into a CDK program use this command: 394 | ```bash 395 | npm run build 396 | ``` 397 | Let us use the us-east-1 region. 398 | ```bash 399 | export AWS_REGION=us-east-1 400 | ``` 401 | To create the initial CDK infrastructure in your AWS account in the specified region (us-east-1 in this example), run the [cdk bootstrap](https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html) command as such: 402 | 403 | ```bash 404 | cdk bootstrap 405 | ``` 406 | The CDK uses the same supporting infrastructure for all projects within a region, so you only need to run the bootstrap command once in any region in which you create CDK stacks. 407 | 408 | Finally deploy the stack using this command: 409 | ```bash 410 | cdk deploy 411 | ``` 412 | (Enter “y” in response to Do you wish to deploy all these changes (y/n)?). 413 | 414 | **Tip –** If you get stuck on an inexplicable error, check package.json and confirm that all CDK libraries have the same version number (with no leading caret ^). Many mysterious CDK project errors stem from mismatched versions. If necessary, correct the version numbers, delete the package-lock.json file and node_modules tree and run npm install. 415 | 416 | The syntax and additional details of these commands are documented [here](https://docs.aws.amazon.com/cdk/latest/guide/cli.html#cli-commands). 417 | 418 | 419 | 420 | ## Test the HTTP API 421 | Take a note of the HTTP API endpoints of the List and Get Lambda Functions as shown above. Using the Cloud9 terminal run the following commands: 422 | ```bash 423 | curl -s https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/list | jq 424 | 425 | curl -s https://xxxxxxxxx.execute-api.us-east-1.amazonaws.com/get/2013/Rush | jq 426 | ``` 427 | 428 | 429 | 430 | ## AWS API Gateway (AWS Management Console) 431 | Here is the integration of the HTTP API with the backend Lambda functions inside the AWS Management Console. 432 | 433 | 434 | 435 | ## AWS Lambda 1ms billing 436 | On December 1, 2020 AWS Lambda [reduced the billing granularity for Lambda function duration from 100ms down to 1ms](https://aws.amazon.com/about-aws/whats-new/2020/12/aws-lambda-changes-duration-billing-granularity-from-100ms-to-1ms/#:~:text=AWS%20Lambda%20reduced%20the%20billing,100%20ms%20increment%20per%20invoke.). This will lower the price for most Lambda functions, more so for short duration functions. Their compute duration will be billed in 1ms increments instead of being rounded up to the nearest 100 ms increment per invocation. 437 | 438 | AWS Lambda reduced the billing granularity for Lambda function duration from 100ms down to 1ms. This will lower the price for most Lambda functions, more so for short duration functions. Their compute duration will be billed in 1ms increments instead of being rounded up to the nearest 100 ms increment per invocation. 439 | 440 | 441 | 442 | ## Cleanup 443 | 444 | To clean up the resources created by the CDK, run the following commands in a terminal of your Cloud9 instance: 445 | ```bash 446 | cd ~/environment/aws-cdk-lambda-container/cdk 447 | cdk destroy 448 | ``` 449 | (Enter “y” in response to: Are you sure you want to delete (y/n)?). 450 | 451 | To clean up the Movies DynamoDB table created manually, run the following command: 452 | ```bash 453 | aws dynamodb --region us-east-1 delete-table --table-name Movies 454 | ``` 455 | 456 | ## Conclusion 457 | 458 | The AWS Cloud Development Kit (AWS CDK) lets developers define their cloud infrastructure as code in one of five supported programming languages instead of JSON or YAML. In order to create complex and complete architectures, AWS CDK saves developers time and effort in writing code using one of the scripting languages of their choice. 459 | 460 | 461 | -------------------------------------------------------------------------------- /cdk/bin/http-api-aws-lambda-container.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { HttpApiAwsLambdaContainerStack } from '../lib/http-api-aws-lambda-container-stack'; 5 | 6 | if (!process.env.AWS_REGION) { 7 | console.error("Please specify the AWS region with the AWS_REGION environment variable"); 8 | process.exit(1); 9 | } 10 | 11 | const env = { region: process.env.AWS_REGION }; 12 | 13 | const app = new cdk.App(); 14 | new HttpApiAwsLambdaContainerStack(app, 'HttpApiAwsLambdaContainerStack', { env: env }); 15 | -------------------------------------------------------------------------------- /cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/http-api-aws-lambda-container.ts", 3 | "context": { 4 | "@aws-cdk/core:enableStackNameDuplicates": "true", 5 | "aws-cdk:enableDiffNoFail": "true", 6 | "@aws-cdk/core:stackRelativeExports": "true" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /cdk/lib/http-api-aws-lambda-container-stack.ts: -------------------------------------------------------------------------------- 1 | // import * as cdk from '@aws-cdk/core'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as iam from '@aws-cdk/aws-iam'; 4 | import * as lambda from '@aws-cdk/aws-lambda'; 5 | import * as apig from '@aws-cdk/aws-apigatewayv2'; 6 | import * as apigintegration from '@aws-cdk/aws-apigatewayv2-integrations'; 7 | import * as dynamodb from '@aws-cdk/aws-dynamodb'; 8 | const path = require('path'); 9 | 10 | export class HttpApiAwsLambdaContainerStack extends cdk.Stack { 11 | 12 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 13 | super(scope, id, props); 14 | 15 | const tableName = 'Movies'; 16 | 17 | //Import existing Movies DynamoDB table 18 | 19 | const table = dynamodb.Table.fromTableName(this, 'MoviesTable', 'Movies'); 20 | 21 | //AWS Lambda Functions 22 | 23 | const listMovieFunction = new lambda.DockerImageFunction(this, 'listMovieFunction',{ 24 | functionName: 'listMovieFunction', 25 | code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), { 26 | cmd: [ "list.list" ], 27 | entrypoint: ["/lambda-entrypoint.sh"], 28 | }), 29 | environment: { 30 | DYNAMODB_TABLE: tableName 31 | }, 32 | }); 33 | 34 | const getMovieFunction = new lambda.DockerImageFunction(this, 'getMovieFunction',{ 35 | functionName: 'getMovieFunction', 36 | code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, '../../src/movie-service'), { 37 | cmd: [ "get.get" ], 38 | entrypoint: ["/lambda-entrypoint.sh"], 39 | }), 40 | environment: { 41 | DYNAMODB_TABLE: tableName 42 | }, 43 | }); 44 | 45 | //CloudWatch Logs Policy 46 | 47 | const cloudWatchLogsPolicyPolicy = new iam.PolicyStatement({ 48 | effect: iam.Effect.ALLOW, 49 | actions: [ 50 | "logs:CreateLogGroup", 51 | "logs:CreateLogStream", 52 | "logs:PutLogEvents", 53 | ] 54 | }); 55 | 56 | cloudWatchLogsPolicyPolicy.addAllResources(); 57 | 58 | //Grant CloudWatch access to Lambda Functions 59 | 60 | listMovieFunction.addToRolePolicy(cloudWatchLogsPolicyPolicy); 61 | getMovieFunction.addToRolePolicy(cloudWatchLogsPolicyPolicy); 62 | 63 | //Grant ReadWrite access to Lambda Functions 64 | 65 | table.grantReadWriteData(listMovieFunction); 66 | table.grantReadWriteData(getMovieFunction); 67 | 68 | // Lambda Integrations 69 | 70 | const listMovieFunctionIntegration = new apigintegration.LambdaProxyIntegration({ 71 | handler: listMovieFunction, 72 | }); 73 | 74 | const getMovieFunctionIntegration = new apigintegration.LambdaProxyIntegration({ 75 | handler: getMovieFunction, 76 | }); 77 | 78 | //Http Api 79 | 80 | const httpApi = new apig.HttpApi(this, "httpApi", { 81 | apiName: "httpApi", 82 | createDefaultStage: true, 83 | }); 84 | 85 | //Http Api Routes 86 | 87 | httpApi.addRoutes({ 88 | integration: listMovieFunctionIntegration, 89 | methods: [apig.HttpMethod.GET], 90 | path: '/list', 91 | }); 92 | 93 | httpApi.addRoutes({ 94 | integration: getMovieFunctionIntegration, 95 | methods: [apig.HttpMethod.GET], 96 | path: '/get/{year}/{title}', 97 | }); 98 | 99 | // API and Service Endpoints 100 | 101 | const httpApiEndpoint = httpApi.apiEndpoint; 102 | const listMovieFunctionEndpoint = httpApiEndpoint + "/list"; 103 | const getMovieFunctionEndpoint = httpApiEndpoint + "/get/{year}/{title}"; 104 | 105 | new cdk.CfnOutput(this, "Http Api endpoint: ", { 106 | value: httpApiEndpoint, 107 | }); 108 | 109 | new cdk.CfnOutput(this, "Http Api endpoint - listMovieFunction : ", { 110 | value: listMovieFunctionEndpoint, 111 | }); 112 | 113 | new cdk.CfnOutput(this, "Http Api endpoint - getMovieFunction : ", { 114 | value: getMovieFunctionEndpoint, 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "http-api-blog-cdk-latest", 3 | "version": "0.1.0", 4 | "bin": { 5 | "http-api-blog-cdk-latest": "bin/http-api-blog-cdk-latest.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "1.109.0", 15 | "@aws-cdk/aws-apigatewayv2-integrations": "1.109.0", 16 | "@aws-cdk/aws-dynamodb": "1.109.0", 17 | "@aws-cdk/aws-ec2": "1.109.0", 18 | "@aws-cdk/aws-ecr": "1.109.0", 19 | "@aws-cdk/aws-ecs": "1.109.0", 20 | "@aws-cdk/aws-elasticloadbalancingv2": "1.109.0", 21 | "@aws-cdk/aws-iam": "1.109.0", 22 | "@aws-cdk/aws-logs": "1.109.0", 23 | "@types/jest": "^26.0.10", 24 | "@types/node": "10.17.27", 25 | "aws-cdk": "1.109.0", 26 | "jest": "^26.4.2", 27 | "ts-jest": "^26.2.0", 28 | "ts-node": "^8.1.0", 29 | "typescript": "~3.9.7" 30 | }, 31 | "dependencies": { 32 | "@aws-cdk/aws-apigatewayv2": "^1.109.0", 33 | "@aws-cdk/core": "1.109.0", 34 | "path": "^0.12.7", 35 | "source-map-support": "^0.5.16" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /images/API_Gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/API_Gateway.png -------------------------------------------------------------------------------- /images/Architecture-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/Architecture-1.png -------------------------------------------------------------------------------- /images/Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/Architecture.png -------------------------------------------------------------------------------- /images/Cloud9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/Cloud9.png -------------------------------------------------------------------------------- /images/Cloud9Install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/Cloud9Install.png -------------------------------------------------------------------------------- /images/Docker-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/Docker-build.png -------------------------------------------------------------------------------- /images/DynamoDB-Load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/DynamoDB-Load.png -------------------------------------------------------------------------------- /images/DynamoDB-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/DynamoDB-create.png -------------------------------------------------------------------------------- /images/DynamoDB-create_censored.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/DynamoDB-create_censored.jpg -------------------------------------------------------------------------------- /images/GetFunction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/GetFunction.png -------------------------------------------------------------------------------- /images/GetFunction_censored.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/GetFunction_censored.jpg -------------------------------------------------------------------------------- /images/Lambda-1ms-Billing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/Lambda-1ms-Billing.png -------------------------------------------------------------------------------- /images/cdk-deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/cdk-deploy.png -------------------------------------------------------------------------------- /images/cdk-deploy_censored.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-lambda-container/e621a847d4f986431fa3bfe618147bf5a27ff6f6/images/cdk-deploy_censored.jpg -------------------------------------------------------------------------------- /src/movie-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/lambda/nodejs:12 2 | # Alternatively, you can pull the base image from Docker Hub: amazon/aws-lambda-nodejs:12 3 | 4 | # Copy the Lambda functions 5 | COPY list.js get.js package.json package-lock.json ${LAMBDA_TASK_ROOT}/ 6 | 7 | # Install NPM dependencies for function 8 | RUN npm install 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/movie-service/get.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AWS = require('aws-sdk'); 4 | const log = require('lambda-log'); 5 | 6 | const dynamoDb = new AWS.DynamoDB(); 7 | 8 | module.exports.get = (event, context, callback) => { 9 | var params = { 10 | TableName: process.env.DYNAMODB_TABLE, 11 | Key: { 12 | "year": { "N": event.pathParameters.year }, 13 | "title": { "S": event.pathParameters.title.toString() } 14 | } 15 | }; 16 | 17 | log.options.debug = true; 18 | log.debug(params); 19 | 20 | // fetch Movie from the database 21 | dynamoDb.getItem(params, (error, result) => { 22 | // handle potential errors 23 | if (error) { 24 | console.error(error); 25 | callback(null, { 26 | statusCode: error.statusCode || 501, 27 | headers: { 'Content-Type': 'text/plain' }, 28 | body: 'Couldn\'t fetch the Movie.', 29 | }); 30 | return; 31 | } 32 | 33 | // create a response 34 | const response = { 35 | statusCode: 200, 36 | body: JSON.stringify(result.Item), 37 | }; 38 | log.debug(response); 39 | callback(null, response); 40 | }); 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /src/movie-service/list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AWS = require('aws-sdk'); 4 | // const DynamoDB = require('aws-sdk/clients/dynamodb') 5 | // const documentClient = new DynamoDB.DocumentClient(); 6 | const log = require('lambda-log'); 7 | 8 | const dynamoDb = new AWS.DynamoDB(); 9 | const params = { 10 | TableName: process.env.DYNAMODB_TABLE, 11 | }; 12 | 13 | module.exports.list = (event, context, callback) => { 14 | log.options.debug = true; 15 | log.debug(params); 16 | 17 | // fetch all Movies from the database 18 | 19 | dynamoDb.scan(params, (error, result) => { 20 | 21 | // handle potential errors 22 | if (error) { 23 | console.error(error); 24 | callback(null, { 25 | statusCode: error.statusCode || 501, 26 | headers: { 'Content-Type': 'text/plain' }, 27 | body: 'Couldn\'t fetch the Movies.', 28 | }); 29 | return; 30 | } 31 | 32 | // create a response 33 | const response = { 34 | statusCode: 200, 35 | body: JSON.stringify(result.Items), 36 | }; 37 | log.debug(response); 38 | callback(null, response); 39 | }); 40 | }; -------------------------------------------------------------------------------- /src/movie-service/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movie-service", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "fast-safe-stringify": { 8 | "version": "2.0.7", 9 | "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 10 | "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" 11 | }, 12 | "lambda-log": { 13 | "version": "2.4.0", 14 | "resolved": "https://registry.npmjs.org/lambda-log/-/lambda-log-2.4.0.tgz", 15 | "integrity": "sha512-py9aD8eHM4gx20t8LQ1UZfWSuixtpjFB/SzGwUoeKoELkekR6SzTIokS6vqlT8OmRBVuZFLiZbj+JQ8YCnKATg==", 16 | "requires": { 17 | "fast-safe-stringify": "^2.0.6" 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/movie-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movie-service", 3 | "version": "1.0.0", 4 | "description": "This is a sample movie-service", 5 | "main": "get.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "MIT-0", 12 | "dependencies": { 13 | "lambda-log": "^2.4.0" 14 | } 15 | } 16 | --------------------------------------------------------------------------------