Real world example

Hi all,

Does anyone have a real-world example using node and serverless v1 I could look at?
A couple of things still confuse me:

  • What is a good folder structure for a serverless project with multiple functions grouped in multiple files (in the example of a blog I would have one file for CRUD endpoints for posts, another for comments, etc…)
  • How to use external node modules? Where do they need to be for Lambda to find them
  • How to use custom libraries/modules with serverless/lambda. I’d like to keep my “functions” files as lean as possible and have all the logic in “lib” files to make them easier to test.

I’m still new to node, lambda and serverless, and I’m having a hard time figuring this out by myself.

Thanks

3 Likes

ok, I just found out that the reason point 2 and 3 were not working for me was because of a bug in the packaging plugin when using serverless on Windows. A pull request fixing this has been merged today and it now seems to work if I duplicate the changes on my side.

It would be great to have some samples of recommended directory structure as requested in #1. I have the same question as a newcomer to serverless. The sample template is just a bit too simple.

OK. I found one that is close to what I was looking for.
https://github.com/zanon-io/aws-serverless-demo

It is for v0.5, though. I’d love to see what the current thinking is in the context of v1.

1 Like

agreed with points above. I’m a serverless noob and I can make one service work as the docs have walked me through. I think that simple example was needed as I tried to get into serverless 0.5 and it was too confusing. Now at least I have my foot in the door and I understand a bit of what is happening here. However, moving forward I could use a ‘pet store’ example to show me best practices for building out a bigger solution.

Thanks for the questions, we will add more documentation and examples in the near future as docs are one of our highest priorities now that we’ve gotten most of the features for v1.0 implemented.

Basically you want to get all the dependencies through npm into your node_modules folder in the same directory. You can also have a lib folder in the same directory as your serverless.yml which will get automatically packaged and you can reference it in your other files.

@fruffin I found this article: https://cloudonaut.io/create-a-serverless-restful-api-with-the-serverless-framework-powered-by-api-gateway-lambda-and-dynamodb/

Take a look, I think it could help.

Best Regards,

Cesar Oliveira

Not sure if our use case should be used as a reference, but we are working on a small to medium sized REST API (something around 15 resources with an average of 2 methods per resource + CORS). Our workflow/design may present future flaws and changes, but we are confident of the path.

This is our “third tour” with API Gateway + Lambdas. We were early adopters since day 1 and started with our own deployment, at first all by hand. In our “second tour” we wrote our own scripts in gulp. At that time there were no Serverles or Apex. Now we are confident that Serverless, being targeted and supported by AWS would be the best choice and we are re-writing everything with the Beta version. We have no intention to use other cloud services like Google, IBM or Azure. So multi-provider is not a value in our opinion.

Comments and suggestions are always welcomed. :wink:

Our project rely heavily in other AWS services like IAM, S3, SQS, Kinesis, Cognito, and a lot more (really, really a lot!!). So our desire to integrate Serverless project with Cloudformation are high but I’m not sure how much this will pan out in reality (more about this at the end).

We tend to break our services in microservices, not nanoservices. The breakdown is done considering operational aspects, (ie, a balance between how easy it is to deploy a new full stage) instead of following the “academical definitions” of microservices.

Our API has a centralized authentication strategy (we have our own OAuth2 implementation), so we have a token endpoint that handles all authentication processes and issues JWT token containing action based permissions plus context information, all data signed and encrypted. Once clients get a token they call whichever resource endpoints they need always sending this token. Each resource endpoint validates the JWT token before further processing the request. The token has information about which actions the client/user are allowed but Its up to the resource to decide if they want enforce it or not.

Even thou this process is common to every lambda function, we do not implement it as an authorizer. Those are only used when the API Gateway integrates directly to another AWS services, bypassing the lambda function. In this scenario we use authorizers to provision proper AWS credentials, otherwise everything else (in regards to IAM permissions) are declared directly on the Lambda execution roles.

Since we trace a lot of calls trough Cloudwatch, we pass along the request-id generated by the API Gateway to track other services brokers like SQS/SNS and so for.

Our projects are all nodejs based. A single project here is usually composed of multiple Serverless Functions with a single root Serverless.yml. (easy to deploy, remember?) This is also possible because most of our functions use the same node modules, hence there is very few gain to have each function handled by its own Serverless.yaml

Our project structure looks like this:

- project-dir/
     - serverless.yaml
     - functions/
          - users.js
          - user.js
      - lib/
        - our common helpers.js
     - test/
         - fixtures/
         - specs/
               - user.spec.js
               - users.spec.js
               - lib-helper.js
               - lib-other-helper.js
          - e2e/
              - case1.e2e.js
              - case2.e2e.js
     - migrations
     - resources
           - cloudformation.templates.json

We have functions for collections resources and “entity” resources (ie: USERS and USER).
We avoid creating too much directories, since in the past we ended with a mess. This also helps visualize when we are creating complex solutions/dependencies. Simple is the new black. :wink:

We do BDD and E2E but not TDD. Thats due to AWS services dependencies that in the end forces us to develop in deployed stages most of the time. Mocking AWS service is done but in the end still very limited.

We are still considering if in future the Serverless.yaml will manage its AWS resources trough CF templates. We are keen because it enable us to create new stacks for whatever reason, but it also risky because removing the wrong stage may throw away resources that cannot be easily recovered (like RDS, S3 content, ES clusters). Also we had some headache with CF stacks updates/rollbacks.

As of today AWS services are managed manually through CloudFormation. Ie, we use CF as way to document and keep everything reproducible but we manually apply them in CF. We try to do not update CF stacks as much as possible for the mentioned reasons.

We use environment variables loaded in runtime to carry parameters and some sensitive information. Sensitive parameters are each day less and less present due to use of IAM Roles and credentials.

We are still fighting in regards to dynamic debug levels. Cloduwatch is sometimes a bless and other times a real pain. So we do not kept production with a verbose debug mode, but since those are controlled by a .env file, when we need to elevate the verboseness a re-deploy is required. Far from ideal. The use of a shared resource like an S3 file or KeyValue service does not work due to time to load and failure risks they impose. As I said haven’t found yet a good solution for this problem.

Hope that this information helps in anyway.

Cheers,

Eric

6 Likes

I have been working recently with v1.0 of a private npm registry that works with npm cli and replaces its login using github / github enterprise. Mainly for a use case for a client I am working on site with at the moment for internal modules they wish to keep private.

Hopefully the repo helps some people with some pointers around v1 until the documentation is updated. Also feedback welcome as still learning this as we go with everyone! :smiley:

The proper folder structure of Serverless Framework is what I really want to know nowadays :grinning:

I just simply designed my project structure like below. Is this structure good or bad? I want to know whether this design has problems and how can I organize my folders in a proper way.

myAPI
  |__ node_modules
  |__ pakcage.json         
  |
  |__ weatherService       // Serverless Service      
    |__ serverless.yml     // weatherService configuration file
    |__lib                 // weather service shared code
    |__update              // update endpoint
        |__ handler.js
        |__ event.json     // event.json for weatherService/update 
    |__retrieve            // retrieve endpoint
        |__ handler.js
        |__ event.json     // event.json for weatherService/retrieve   
  |  
  |__ anotherService       // Another Serverless Service      
    |__ serverless.yml     // anotherService configuration file
    |__ ...

@yuhwan the folder design looks good, but going forward we don’t really have a very strict folder structure. Basically you can build separate services and every service is its own folder. If you want to do this in one repo, or every service gets its own repo is completely up to you and you can easily change this in the future once you e.g. want to move a service out into its own repo.

Hey,

I to have a question around the folder structure, I have a CRUD API that was created in an older version of serverless where by I have a data access library that was ultimately shared by all CRUD functions. I used the magic handlers feature so that all CRUD functions could find the lib folder. Now that I’m going to try and migrate my API to the new version of Serverless I am slightly confused. It feels like I now need to create a single Serverless Service at root level which maps out all my CRUD functions across the API so that a single lib folder can be shared across them. However I feel like I should be setting it up so that each CRUD endpoint is a Serverless Service but then I can’t see how they can access the lib folder from root level.

The old structure was like this

API
|__ Users // Handler for CRUD around Users
|__ Contacts // Handler for CRUD around Contacts
|__ Tasks // Handler for CRUD around Task
|
|__ Lib // Serverless Service
|__ Data Access // Lots of data access stuff
|__ Error Handling

Any advance here would be helpful as I would love to stay up to date with the amazing work you are doing

For shared libs we want users to rely on their native package management, e.g. NPM in this case. So the idea is that your shared lib is either in a separate repo and gets installed as a dependency or you use npm link (https://docs.npmjs.com/cli/link) if its in the same repo.

This makes sure you have a clean setup of your dependencies and we don’t introduce a special way how you can or should build dependencies of your projects.

1 Like

Thank you Eric, I was struggling figuring out how to structure my project in order to make it testable. Would you mind posting a copy of your serverless.yaml file please? I’m getting this error "errorMessage": "Handler 'get' missing on module 'session'" and I can’t seem to figure out the correct config/structure for this.

Hi Patrax, sorry for my long delay on getting back to you. I was away for a few weeks.

My serverless.yml is a bit outdated and it may worth a double-check given latest releases. I also redacted some sentive information (the ones in angled brackets <>) but kept the structure as is so you could understand it.

Best regards,

service: sample-api

provider:
  name: aws
  runtime: nodejs4.3
  vpc:
    securityGroupIds:
      - <sg-group-id>
    subnetIds: 
      - <subnet-subnet-id>

defaults:
  stage: dev
  region: us-east-1

# we are reviewing package process due to recent improvements in serverless
package:
  include:
    - .env
    - lib

  exclude:
    - test
    - config.dev.js
    - config.local.js
    - config.js
    - serverless.env.yml
    - .env.dev
    - .env.local
    - .env.production
    - migrations
    - docker-compose.yml

functions:
  authorizer:
    handler: functions/authorizer.handler

  token:
    handler: functions/token.handler
    events:
      - http:          
          method: post 
          path: token
          cors:
            origins:
              - '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - x-client-id
  users:
    handler: functions/users.handler
    events:
      - http: get users
      - http:
          method: post
          path: users
          cors:
            origins:
              - '*'
            headers:
              - Content-Type
              - X-Amz-Date
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - x-client-id
  user:
    handler: functions/user.handler
    events:
      - http: get user/{handler}
      - http: patch user/{handler}

resources:
  Resources:
    AWSLambdaVPCAccessExecutionRole:
      Type: AWS::IAM::ManagedPolicy
      Properties:
        Description: Policy for allowing vpc connection.
        Roles:
          - {"Ref" : "IamRoleLambda"}
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            Effect: Allow
            Action:
                - "logs:CreateLogGroup"
                - "logs:CreateLogStream"
                - "logs:PutLogEvents"
                - "ec2:CreateNetworkInterface"
                - "ec2:DescribeNetworkInterfaces"
                - "ec2:DeleteNetworkInterface"
            Resource: "*"
3 Likes

IMO the documentation and templates are as clear as they can be. They allow readers to understand the functionality of the framework without obscuring the concepts with structure. In this way it it is clear that project structure is a personal/team choice.

Rather than more documentation, perhaps a sticky post should be added to the forum so that the above contributors (@ericchaves) and others can post links to their demo repositories without being obscured by more project specific questions.

Perhaps in time, a single page in the Docs could be added with links to theses demo repositories.


@Cloud

There are significant differences between v0.5.x and v1.0 when it comes to packaging lambdas. Perhaps this forum post will help you

1 Like

And why exactly it is bad to have suggested project structure in documentation? I love how angular2 does it. Clear documentation on how things are, naming rules, best practices, etc. It’s clear for everyone. And there’s a standard you can expect when you go into any a2 project.
If you don’t want to you don’t need to follow it, it’s optional.
I fail to see the negative in that.

Hi all, we have created a serverless-react-boilerplate initial version which focuses upon offline development with AWS Lambda, API Gateway and Dynamodb. The boilerplate consists of CRUD operations for a To-do application and a simple react web client that utilizes the api.

Agree with @flomotlik - for nodejs services, make extensive use of private (published to npm as private) or local custom npm modules and npm link. If you make your functions in those modules as “pure” as possible, writing tests using something as simple as tape is quick and easy.

Try to separate I/O operations or hard to mock things into their own packages or the serverless handlers themselves to deal with the parts of the service your building that is specific to AWS.

Done this way, development iterations can be as fast as running the tests, or opening the node repl (just running node in your terminal`) and requiring in the package you’re building to play with it. This also catches syntax errors immediately.

If you move in this direction, you should experience significantly less deploy/invoke iterations during development, and less waiting on CloudWatch logs to show up.

here are a bunch of real world examples! https://github.com/serverless/serverless#services-v10

1 Like