├── .gitignore ├── README.md ├── handler.js ├── package-lock.json ├── package.json ├── postman_collection └── lambda-email.postman_collection.json └── serverless.yml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node,code,serverless 3 | # Edit at https://www.gitignore.io/?templates=node,code,serverless 4 | 5 | ### Code ### 6 | .vscode/* 7 | !.vscode/settings.json 8 | !.vscode/tasks.json 9 | !.vscode/launch.json 10 | !.vscode/extensions.json 11 | 12 | ### Node ### 13 | # Logs 14 | logs 15 | *.log 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | lerna-debug.log* 20 | 21 | # Diagnostic reports (https://nodejs.org/api/report.html) 22 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 23 | 24 | # Runtime data 25 | pids 26 | *.pid 27 | *.seed 28 | *.pid.lock 29 | 30 | # Directory for instrumented libs generated by jscoverage/JSCover 31 | lib-cov 32 | 33 | # Coverage directory used by tools like istanbul 34 | coverage 35 | 36 | # nyc test coverage 37 | .nyc_output 38 | 39 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 40 | .grunt 41 | 42 | # Bower dependency directory (https://bower.io/) 43 | bower_components 44 | 45 | # node-waf configuration 46 | .lock-wscript 47 | 48 | # Compiled binary addons (https://nodejs.org/api/addons.html) 49 | build/Release 50 | 51 | # Dependency directories 52 | node_modules/ 53 | jspm_packages/ 54 | 55 | # TypeScript v1 declaration files 56 | typings/ 57 | 58 | # Optional npm cache directory 59 | .npm 60 | 61 | # Optional eslint cache 62 | .eslintcache 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | 80 | # next.js build output 81 | .next 82 | 83 | # nuxt.js build output 84 | .nuxt 85 | 86 | # vuepress build output 87 | .vuepress/dist 88 | 89 | # Serverless directories 90 | .serverless/ 91 | 92 | # FuseBox cache 93 | .fusebox/ 94 | 95 | # DynamoDB Local files 96 | .dynamodb/ 97 | 98 | ### Serverless ### 99 | # Ignore build directory 100 | .serverless 101 | 102 | # End of https://www.gitignore.io/api/node,code,serverless 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-node-simple-messaging 2 | 3 | Simple AWS lambda serverless function for sending emails using AWS SES 4 | 5 | ## Prerequisites 6 | - [Get Amazon SES Production Access] (https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html) 7 | 8 | ## Setup 9 | Run the following commands 10 | ```sh 11 | $ npm install -g serverless # Install serverless globally 12 | $ serverless config credentials --provider aws --key --secret # Setting up default aws credentials 13 | $ cd aws-serverless-messaging 14 | $ npm install # Installing dependency 15 | ``` 16 | 17 | ## Deployment 18 | ```sh 19 | $ serverless deploy # Deploying serverless function to aws 20 | ``` 21 | 22 | By this command `serverless deploy` you should be able to see the lambda function in your aws lambda dashboard and it should have returned an endpoint and api_key in your terminal keep these for now. 23 | 24 | Setup the following variables into your aws lambda function 25 | - ACCESS_KEY_ID (AWS account access key) 26 | - SECRET_ACCESS_KEY (AWS account secret key) 27 | - API_VERSION (API version for AWS SES) 28 | - REGION (AWS SES region as it's not supported in all the region for now) 29 | 30 | ## Running 31 | 32 | Run ```export MY_API_KEY=``` 33 | 34 | Make a POST API call to the endpoint and send x-api-key into headers with the api_key value returned after deploy command. The API supports the following body parameters 35 | - toAddresses (Array of valid email addresses) 36 | - bccAddresses (Array of valid email addresses, optional) 37 | - ccAddresses (Array of valid email addresses, optional) 38 | - textBody (Text email body) 39 | - body (HTML email body) 40 | - subject (Email subject) 41 | - sender (Sender email) 42 | 43 | For example - 44 | ``` 45 | { 46 | "toAddresses": ["toemail@example.com"], 47 | "textBody": "Hello", 48 | "body": "Hello", 49 | "subject": "Subject" 50 | "sender": "senderemail@example.com" 51 | } 52 | ``` 53 | 54 | ## Running by postman collection 55 | 56 | - Import the postman collection and set the endpoint and x-api-key and make a hit. 57 | 58 | ## Contributors 59 | 60 | [Sparsh Pipley](https://in.linkedin.com/in/sparsh-pipley-6ab0b1a4/) 61 | 62 | ## License 63 | 64 | Built under [MIT](http://www.opensource.org/licenses/mit-license.php) license. 65 | 66 | -------------------------------------------------------------------------------- /handler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let AWS = require('aws-sdk'); 4 | let ses = new AWS.SES({ 5 | apiVersion: process.env.API_VERSION, 6 | accessKeyId: process.env.ACCESS_KEY_ID, 7 | secretAccessKey: process.env.SECRET_ACCESS_KEY, 8 | region: process.env.REGION 9 | }); 10 | 11 | module.exports.sendEmail = async (event) => { 12 | let body = JSON.parse(event.body); 13 | 14 | let params = { 15 | Destination: { 16 | CcAddresses: body.ccAddresses || [], 17 | BccAddresses: body.bccAddresses || [], 18 | ToAddresses: body.toAddresses 19 | }, 20 | Message: { 21 | Body: { 22 | Html: { 23 | Data: body.body, 24 | Charset: 'utf-8' 25 | }, 26 | Text: { 27 | Data: body.textBody, 28 | Charset: 'utf-8' 29 | } 30 | }, 31 | Subject: { 32 | Data: body.subject, 33 | Charset: 'utf-8' 34 | } 35 | }, 36 | Source: body.sender 37 | }; 38 | 39 | return await ses.sendEmail(params).promise().then(function (data) { 40 | return { 41 | statusCode: 200, 42 | body: JSON.stringify({ 43 | message: data 44 | }), 45 | }; 46 | }).catch(function (err) { 47 | return { 48 | statusCode: err.statusCode || 500, 49 | body: JSON.stringify({ 50 | message: err, 51 | }), 52 | }; 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "aws-sdk": { 6 | "version": "2.444.0", 7 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.444.0.tgz", 8 | "integrity": "sha512-3vdC7l5BJ3zHzVNgtIxD+TDviti/sAA/1T8zAXAm2XhZ7AePR5lYIMNAwqu+J44Nm6PFSK1QNSzRQ6A4/6b9eA==", 9 | "requires": { 10 | "buffer": "4.9.1", 11 | "events": "1.1.1", 12 | "ieee754": "1.1.8", 13 | "jmespath": "0.15.0", 14 | "querystring": "0.2.0", 15 | "sax": "1.2.1", 16 | "url": "0.10.3", 17 | "uuid": "3.3.2", 18 | "xml2js": "0.4.19" 19 | } 20 | }, 21 | "base64-js": { 22 | "version": "1.3.0", 23 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", 24 | "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" 25 | }, 26 | "buffer": { 27 | "version": "4.9.1", 28 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 29 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 30 | "requires": { 31 | "base64-js": "^1.0.2", 32 | "ieee754": "^1.1.4", 33 | "isarray": "^1.0.0" 34 | } 35 | }, 36 | "events": { 37 | "version": "1.1.1", 38 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 39 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 40 | }, 41 | "ieee754": { 42 | "version": "1.1.8", 43 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", 44 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" 45 | }, 46 | "isarray": { 47 | "version": "1.0.0", 48 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 49 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 50 | }, 51 | "jmespath": { 52 | "version": "0.15.0", 53 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 54 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 55 | }, 56 | "punycode": { 57 | "version": "1.3.2", 58 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 59 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 60 | }, 61 | "querystring": { 62 | "version": "0.2.0", 63 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 64 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 65 | }, 66 | "sax": { 67 | "version": "1.2.1", 68 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 69 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 70 | }, 71 | "url": { 72 | "version": "0.10.3", 73 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 74 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 75 | "requires": { 76 | "punycode": "1.3.2", 77 | "querystring": "0.2.0" 78 | } 79 | }, 80 | "uuid": { 81 | "version": "3.3.2", 82 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 83 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 84 | }, 85 | "xml2js": { 86 | "version": "0.4.19", 87 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 88 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 89 | "requires": { 90 | "sax": ">=0.6.0", 91 | "xmlbuilder": "~9.0.1" 92 | } 93 | }, 94 | "xmlbuilder": { 95 | "version": "9.0.7", 96 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 97 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-messaging", 3 | "version": "1.0.0", 4 | "description": "AWS lambda sample for sending emails using AWS SES", 5 | "main": "handler.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://gitlab.com/systango/open-source/aws-serverless-messaging.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "aws-sdk": "^2.536.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /postman_collection/lambda-email.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "6158c038-d0b3-4df4-855c-15319d9baf18", 4 | "name": "lambda-email", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "lambda-email", 10 | "request": { 11 | "method": "POST", 12 | "header": [ 13 | { 14 | "key": "Content-Type", 15 | "name": "Content-Type", 16 | "value": "application/json", 17 | "type": "text" 18 | }, 19 | { 20 | "key": "x-api-key", 21 | "value": "", 22 | "type": "text" 23 | } 24 | ], 25 | "body": { 26 | "mode": "raw", 27 | "raw": "{\n\t\"toAddresses\": [\"toemail@example.com\"],\n\t\"textBody\": \"Hello\",\n\t\"body\": \"Hello\",\n\t\"subject\": \"Subject\",\n\t\"sender\": \"senderemail@example.com\"\n}" 28 | }, 29 | "url": { 30 | "raw": "/dev/sendEmail", 31 | "host": [ 32 | "" 33 | ], 34 | "path": [ 35 | "dev", 36 | "sendEmail" 37 | ] 38 | } 39 | }, 40 | "response": [] 41 | } 42 | ] 43 | } -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: email 2 | 3 | provider: 4 | name: aws 5 | runtime: nodejs8.10 6 | region: ap-south-1 7 | apiKeys: 8 | - ${env:MY_API_KEY} 9 | usagePlan: 10 | quota: 11 | limit: 50 #The maximum number of requests that can be made in a given time period. 12 | offset: 2 #The number of requests subtracted from the given limit in the initial time period. 13 | period: MONTH #The time period in which the limit applies. Valid values are "DAY", "WEEK" or "MONTH". 14 | throttle: 15 | burstLimit: 10 #The maximum API request rate limit over a time ranging from one to a few seconds. The maximum API request rate limit depends on whether the underlying token bucket is at its full capacity. 16 | rateLimit: 10 #The API request steady-state rate limit (average requests per second over an extended period of time) 17 | 18 | functions: 19 | sendEmail: 20 | handler: handler.sendEmail 21 | events: 22 | - http: 23 | path: sendEmail 24 | method: post 25 | cors: true 26 | private: true 27 | --------------------------------------------------------------------------------