Hi!
So it has been a while since I posted some technical posts. Now … this is something that touches us all – the lack of time in the jungle of ongoing projects 🙂 However today we will look into something which I find quite nice for developing of new applications.
Solution is based on serverless framework. Now before we go on – we all know that serverless is a nice catchy word for ‘someone’ else computer and operation problem :)’ . But idea is simple – I’m using AWS – and there it spins me up lambda functions with associated API gateway.
I decided to create this solution to have unified way of deploying and interacting with AWS services in a way that would be easiest for me to consume. However for someone else it might not be the best. Also to be on safe side – this code is really version v1.0.0 so it will get updates as we go ( PR always welcome )
The repository for this write up is available under https://github.com/RafPe/serverless-api-sns
Solution folder structure
The solution structure is created as follows
total 32 -rw-r--r-- 1 rafpe 450652656 1.0K Sep 9 17:49 LICENSE -rw-r--r-- 1 rafpe 450652656 2.2K Sep 10 15:07 README.md drwxr-xr-x 234 rafpe 450652656 7.8K Sep 5 23:53 node_modules -rw-r--r-- 1 rafpe 450652656 255B Sep 10 14:19 package.json -rw-r--r-- 1 rafpe 450652656 3.6K Sep 10 14:21 serverless.yml drwxr-xr-x 7 rafpe 450652656 238B Sep 10 14:52 sns
and the SNS folder:
├── attributes ├── endpoint │ ├── create.js │ ├── delete.js │ └── list.js ├── messages ├── models │ └── endpoint.create.json └── topics
Code
Now since this is not a coding school and I have used really simple code I will not be going into details there. I just might say code has some portions which are repeated and could be wrapped into common methods 😉 did not have time to take a look into that one yet.
For the rest it is using standard aws libraries to execute most of the actions
Serverless.yml
Is the heart of your deployment. It describes what will be created and how to link those things together. For more advanced examples you should check out docs.serverless.com
# Welcome to Serverless! # # This file is the main config file for your service. # It's very minimal at this point and uses default values. # You can always add more config options for more control. # We've included some commented out config examples here. # Just uncomment any of them to get that config option. # # For full config options, check the docs: # docs.serverless.com # # Happy Coding! service: api # You can pin your service to only deploy with a specific Serverless version # Check out our docs for more details # frameworkVersion: "=X.X.X" provider: name: aws role: xmyCustRole1 apiKeys: - myApiKey runtime: nodejs6.10 region: eu-west-1 stage: dev functions: create: handler: sns/endpoint/create.create events: - http: path: endpoint/create method: post cors: true private: true delete: handler: sns/endpoint/delete.delete events: - http: path: endpoint/delete method: delete cors: true private: true list: handler: sns/endpoint/list.list events: - http: path: endpoint/list method: post cors: true private: true resources: Resources: # PetsModelNoFlatten: # Type: "AWS::ApiGateway::Model" # Properties: # RestApiId: {Ref: ApiGatewayRestApi} # ContentType: "application/json" # Description: "Schema for Pets example" # Name: "PetsModelNoFlatten" # Schema: # Fn::Join: # - "" # - # - "{" # - " \"$schema\": \"http://json-schema.org/draft-04/schema#\"," # - " \"title\": \"PetsModelNoFlatten\"," # - " \"type\": \"array\"," # - " \"items\": {" # - " \"type\": \"object\"," # - " \"properties\": {" # - " \"number\": { \"type\": \"integer\" }," # - " \"class\": { \"type\": \"string\" }," # - " \"salesPrice\": { \"type\": \"number\" }" # - " }" # - " }" # - "}" xmyCustRole1: Type: AWS::IAM::Role Properties: Path: /my/cust/path/ RoleName: xmyCustRole1 AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: myPolicyName PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow # note that these rights are given in the default policy and are required if you want logs out of your lambda(s) Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:log-group:/aws/lambda/*:*:* - Effect: Allow # note that these rights are given in the default policy and are required if you want logs out of your lambda(s) Action: - sns:CreatePlatformEndpoint Resource: arn:aws:sns:*:*:* - Effect: "Allow" Action: - "s3:PutObject" Resource: Fn::Join: - "" - - "arn:aws:s3:::" - "Ref" : "ServerlessDeploymentBucket"
IAM policy
To make this all a bit more secure I defined here my specific IAM Role with custom permissions for actions – So if you would need to extend permisions required you would need to look into that resources as well
Validations
In my code you will find that I validate if specific parameters are received from the request. Now this is again something that
- Could be done better by taking this logic out into common functions or …
- even better to use the API gateway validators
I therefore went ahead and created my self json schema using the following online schema generator. With that one done I had to ‘escape’ those characters and then create a policy using serverless resource
resources: Resources: PetsModelNoFlatten: Type: "AWS::ApiGateway::Model" Properties: RestApiId: {Ref: ApiGatewayRestApi} ContentType: "application/json" Description: "Schema for Pets example" Name: "PetsModelNoFlatten" Schema: Fn::Join: - "" - - "{" - " \"$schema\": \"http://json-schema.org/draft-04/schema#\"," - " \"title\": \"PetsModelNoFlatten\"," - " \"type\": \"array\"," - " \"items\": {" - " \"type\": \"object\"," - " \"properties\": {" - " \"number\": { \"type\": \"integer\" }," - " \"class\": { \"type\": \"string\" }," - " \"salesPrice\": { \"type\": \"number\" }" - " }" - " }" - "}"
This is all nice but the problem I experience now is that I cannot in programatic way find out how to apply required body validators to specific methods using serverless. Maybe something I will find out later.
Deploying
Deploying is easy as running
serverless deploy
and the output should look like
Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (37.84 KB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... .......................... Serverless: Stack update finished... Service Information service: api stage: dev region: eu-west-1 api keys: myApiKey: ID0d9P4Vgi82l2YvndLwi81FA63lCup1adNQX7eD endpoints: POST - https://isr61ohvhl.execute-api.eu-west-1.amazonaws.com/dev/endpoint/create DELETE - https://isr61ohvhl.execute-api.eu-west-1.amazonaws.com/dev/endpoint/delete POST - https://isr61ohvhl.execute-api.eu-west-1.amazonaws.com/dev/endpoint/list functions: create: api-dev-create delete: api-dev-delete list: api-dev-list
Fun
Now this is the part I like the most 🙂 Fun starts here when you play around with the working solution. If you got any feedback I would be more than happy to hear about it.
One Comment