I am wondering what approach in designing serverless functions to take, while taking designing a regular server as a point of reference.
With a traditional server, one would focus on defining collections and then CRUD operations one can run on each of them (HTTP verbs such as GET or POST).
For example, you would have a collection of users
, and you can get all records via app.get('/users', ...)
, get specific one via app.get('/users/{id}', ...)
or create one via app.post('/users', ...)
.
How differently would you approach designing a serverless function? Specifically:
- Is there a sense in differentiating between HTTP operations or would you just go with POST? I find it useful to have them defined on the client side, to decide if I want to retry in case of an error (if the operation is idempotent, it will be safe to retry etc.), but it does not seem to matter in the back-end.
- Naming. I assume you would use something like
getAllUsers()
when with a regular server you would define collection ofusers
and then just use GET to specify what you want to do with it. - Size of functions: if you need to do a number of things in the back-end in one step. Would you define a number of small functions, such as
lookupUser()
,endTrialForUser()
(fired if user we got fromlookupUser()
has been on trial longer than 7 days) etc. and then run them one after another from the client (deciding if trial should be ended on the client - seems quite unsafe), or would you just create agetUser()
and then handle all the logic there? - Routing. In serverless functions, we can't really do anything like
.../users/${id}/accountData
. How would you go around fetching nested data? Would you just return a complete JSON every time?
I have been looking for some comprehensive articles on the matter but no luck. Any suggestions?
CodePudding user response:
This is a very broad question that you've asked. Let me try answering it point by point.
Firstly, the approach that you're talking about here is the Serverless API
project approach. You can clone their sample project to get a better understanding of how you can build REST
apis for performing CRUD
operations. Start by installing the SAM cli and then run the following commands.
$ sam init
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Cloning from https://github.com/aws/aws-sam-cli-app-templates
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Multi-step workflow
3 - Serverless API
4 - Scheduled task
5 - Standalone function
6 - Data processing
7 - Infrastructure event management
8 - Machine Learning
Template: 3
Which runtime would you like to use?
1 - dotnetcore3.1
2 - nodejs14.x
3 - nodejs12.x
4 - python3.9
5 - python3.8
Runtime: 2
Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.
Based on your selections, the only dependency manager available is npm.
We will proceed copying the template using npm.
Project name [sam-app]: sample-app
-----------------------
Generating application:
-----------------------
Name: sample-app
Runtime: nodejs14.x
Architectures: x86_64
Dependency Manager: npm
Application Template: quick-start-web
Output Directory: .
Next steps can be found in the README file at ./sample-app/README.md
Commands you can use next
=========================
[*] Create pipeline: cd sample-app && sam pipeline init --bootstrap
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
Comings to your questions point wise:
- Yes, you should differentiate your
HTTP
operations with their suitableHTTP verbs
. This can be configured at the API Gateway and can be checked for in the Lambda code. Check the source code of handlers & thetemplate.yml
file from the project you've just cloned withSAM
.
// src/handlers/get-by-id.js
if (event.httpMethod !== 'GET') {
throw new Error(`getMethod only accepts GET method, you tried: ${event.httpMethod}`);
}
# template.yml
Events:
Api:
Type: Api
Properties:
Path: /{id}
Method: GET
The naming is totally up to the developer. You can follow the same approach that you're following with your regular server project. You can define the handler with name
getAllUsers
orusers
and then set the path of that resource toGET /users
in theAWS API Gateway
. You can choose theHTTP verbs
of your desire. Check this tutorial out for better understanding.Again this up to you. You can create a single Lambda that handles all that logic or create individual Lambdas that are triggered one after another by the client based on the response from the previous API. I would say, create a single Lambda and just return the cumulative response to reduce the number of requests. But again, this totally depends on the UI integration. If your screens demand separate API calls, then please, by all means create individual lambdas.
This is not true. We can have dynamic routes specified in the API Gateway. You can easily set wildcards in your routes by using
{variableName}
while setting the routes in API Gateway.GET /users/{userId}
TheuserId
will then be available at your disposal in the lambda function viaevent.pathParameters
.GET /users/{userId}?a=x
Similarly, you could even pass query strings and access them viaevent.queryStringParameters
in code. Have a look at working with routes.