Link to chapter - https://serverless-stack.com/chapters/configure-dynamodb-in-serverless.html
OS: darwin
Node Version: 8.11.2
Serverless Version: 1.26.0
Problem is I can’t access env variables with process.env.tableQuestions
Function returns error message:
Invalid table/index name. Table/index names must be between 3 and 255 characters long, and may contain only the characters a-z, A-Z, 0-9, ‘_’, ‘-’, and ‘.’
Here is part of serverless.yml file:
custom:
stage: ${opt:stage, self:provider.stage}
tableThroughputs:
prod: 5
default: 1
tableThroughput: ${self:custom.tableThroughputs.${self:custom.stage}, self:custom.tableThroughputs.default}
webpack:
webpackConfig: ./webpack.config.js
includeModules: true
dynamodb:
start:
port: 8000
inMemory: true
migrate: true
seed: true
noStart: true
seed:
test:
sources:
- table: ${env:tableQuestions}
rawsources: [./offline/seeds/questions.json]
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: eu-central-1
environment:
tableQuestions:
Ref: QuestionsTable
tableReplays:
Ref: ReplaysTable
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- "Fn::GetAtt": [ QuestionsTable, Arn ]
- "Fn::GetAtt": [ ReplaysTable, Arn ]
And this is my dynamoDb Resource configuration:
Resources:
QuestionsTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: questionId
AttributeType: S
KeySchema:
- AttributeName: questionId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
TableName: ${self:custom.stage}-questions
ReplaysTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
TableName: ${self:custom.stage}-replays
It looks okay. What does process.env.tableQuestions
& process.env.tableReplays
resolve to in your Lambda if you do a console.log
?
Don’t know. Stuck at function invoke, error message:
{
"statusCode": 500,
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
"body": "{\"message\":\"1 validation error detected: Value '[object Object]' at 'tableName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z0-9_.-]+\"}"
}
I need to check if a problem persits in production enviroment, at the moment I’m trying to invoke functions localy
I can confirm it works in production. But it’s incompatible with serverless-offline plugin
Oh how are you invoking it locally through the serverless-offline
plugin?
Yes. I’m running dynamoDb docker container and serverless-offline plugin to test my api
And what’s the command you are running that gave you the error?
I’m fairly new to this, so I may be incorrect. After some debugging, I discovered that the tableName env variable gets assigned the [object Object] value, maybe because it doesn’t evaluate the ‘Ref’ intrinsic function (Not played around with serverless as much but thought this might be helpful).
Anyways, you can resolve the issue by replacing
tableName: Ref NotesTable
with,
tableName: notes-${self:custom.stage}
Hmmm that is weird. If you’ve configured your resources correctly, it should pick it up. The Ref is simply pointing to ${self:custom.stage}-notes
.
An issue of this nature has already been posted on the Repo issues: https://github.com/serverless/serverless/issues/3080
Following that issue thread, you can install a plugin serverless-export-env to properly initialize all environment variables with Cloudformation template references.
Again, I’m new to this so I’ve encountered this problem when I use the serverless invoke local rather than serverless-offline plugin. Not sure if it works different for the plugin.
That’s a really old issue. Which version of Serverless Framework are you using?
What is the best way to share Dynamo DB across multiple services. According to Amazon, having a single table is idea, which I can do.
I need to modify different parts of the object, hence difference services that interact with the document differently.
How would you setup the resource in a monorepo?
- Separate Resource Service (how does that work in Seed, race condition?)
- Each serverless.yml references the same
/resources/dynamoDb.yml
configuration and import it?
Additionally, do IAM roles for DynamoDB impact all requests in the service? Can you restrict delete actions to the /delete path for example. May need a different user credentials to delete for example?
Sorry still very new to this.
I talked about the resource sharing in the other thread - Organizing Serverless Projects.
But for the IAM roles, it really depends on the level at which you want to control this. You can use Cognito User Groups. Or you can manage these internally in your code if you have some slightly complicated business logic to resolve permissions.
Is there a good documentation resource on configuring DynamoDB in Serverless’s yaml? I’m checking out the docs here: https://serverless.com/framework/docs/providers/aws/guide/resources/ but it’s not very comprehensive
Yeah I listed one in this chapter.
It’s a guide dedicated to DynamoDB - https://www.dynamodbguide.com/.
I’m experiencing the same as @acidfabric and the fix shown by @aaditya-panik is what I had to do to get past it for now. I really dislike the duplicative naming and I’m sure I’m doing something wrong, but I’m not seeing it.
Some background:
- I read through part 1 in the past 2 weeks
- I started with part 2 to create a base project and literally just tonight cloned the repo
- I have made minor modifications to reflect the real-world project that I plan on using it (namely table + field names)
- I’m using WebStorm as my IDE
Here’s how I invoke the method:
serverless invoke local --function create --path mocks/create-event.json
Here’s the error that gets returned:
message: '1 validation error detected: Value \'[object Object]\' at \'tableName\' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z0-9_.-]+',
code: 'ValidationException',
time: 2018-09-08T01:28:37.270Z,
When I do console.log(process.env.TripsTableName)
;, I simply get:
[object Object]
Here is my serverless.yml:
service: routinator
# Use the serverless-webpack plugin to transpile ES6
plugins:
- serverless-webpack
- serverless-offline
# serverless-webpack configuration
# Enable auto-packing of external modules
custom:
# Our stage is based on what is passed in when running serverless
# commands. Or fallsback to what we have set in the provider section.
stage: ${opt:stage, self:provider.stage}
# Set our DynamoDB throughput for prod and all other non-prod stages.
tableThroughputs:
prod: 5
default: 1
tableThroughput: ${self:custom.tableThroughputs.${self:custom.stage}, self:custom.tableThroughputs.default}
# Load our webpack config
webpack:
webpackConfig: ./webpack.config.js
includeModules: true
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-1
# These environment variables are made available to our functions
# under process.env.
environment:
TripsTableName:
Ref: TripsTable
# 'iamRoleStatement' defines the permission policy for the Lambda function.
# In this case Lambda functions are granted with permissions to access DynamoDB.
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
# Restrict our IAM role permissions to
# the specific table for the stage
Resource:
- "Fn::GetAtt": [ TripsTable, Arn ]
functions:
# Defines an HTTP API endpoint that calls the main function in create.js
# - path: url path is /trips
# - method: POST request
# - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
# domain api call
# - authorizer: authenticate using the AWS IAM role
create:
handler: trips/create.main
events:
- http:
path: trips
method: post
cors: true
authorizer: aws_iam
get:
# Defines an HTTP API endpoint that calls the main function in get.js
# - path: url path is /trips/{id}
# - method: GET request
handler: trips/get.main
events:
- http:
path: trips/{id}
method: get
cors: true
authorizer: aws_iam
list:
# Defines an HTTP API endpoint that calls the main function in list.js
# - path: url path is /trips
# - method: GET request
handler: trips/list.main
events:
- http:
path: trips
method: get
cors: true
authorizer: aws_iam
update:
# Defines an HTTP API endpoint that calls the main function in update.js
# - path: url path is /trips/{id}
# - method: PUT request
handler: trips/update.main
events:
- http:
path: trips/{id}
method: put
cors: true
authorizer: aws_iam
delete:
# Defines an HTTP API endpoint that calls the main function in delete.js
# - path: url path is /trips/{id}
# - method: DELETE request
handler: trips/delete.main
events:
- http:
path: trips/{id}
method: delete
cors: true
authorizer: aws_iam
# Create our resources with separate CloudFormation templates
resources:
# DynamoDB
- ${file(resources/dynamodb-tables.yml)}
# S3
- ${file(resources/s3-bucket.yml)}
# Cognito
- ${file(resources/cognito-user-pool.yml)}
Here is my resources/dynamodb-tables.yml:
Resources:
TripsTable:
Type: AWS::DynamoDB::Table
Properties:
# Generate a name based on the stage
TableName: ${self:custom.stage}-trips
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
- AttributeName: tripId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
- AttributeName: tripId
KeyType: RANGE
# Set the capacity based on the stage
ProvisionedThroughput:
ReadCapacityUnits: ${self:custom.tableThroughput}
WriteCapacityUnits: ${self:custom.tableThroughput}
It works when I change serverless.yml to:
environment:
TripsTableName: ${self:custom.stage}-trips
Any thoughts?
At a glance, most of your stuff looks okay. Have you deployed yet or are you just running it locally for now?
Also which version of Serverless are you using?
For two or more tables, how would the the environment block and iamrolestatements Resource definition change?
It would just need more lines in the Resource
section:
# These environment variables are made available to our functions
# under process.env.
environment:
tableName:
Ref: NotesTable
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:DescribeTable
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
# Restrict our IAM role permissions to
# the specific table for the stage
Resource:
- "Fn::GetAtt": [ NotesTable1, Arn ]
- "Fn::GetAtt": [ NotesTable2, Arn ]
- "Fn::GetAtt": [ NotesTable3, Arn ]