Hello, I’m trying to move our API from serverless ...
# guide
n
Hello, I’m trying to move our API from serverless to SST. I don’t find an alternative to invoke lambda locally with mock data like we can do with serverless ( see here https://serverless-stack.com/chapters/invoke-lambda-functions-locally.html ). I know we can use
sst start
but that way I have to hit the API Gateway manually (or with a custom automation). So, as far as I can see, there isn’t a 1 to 1 alternative to
serverless invoke local
, am I right?
f
Hey @Nicholas Odanga Yeah, currently there isn’t. But I think it’s fair to add a CLI command for it. One thing to consider is that the function gets invoked by
sst start
, the IAM credentials from the real Lambda function are passed down to ur local, and the local function is executed using it.
Btw, do you mind sharing a bit on ur usecase? Are you looking for a quick way to invoke ur function when
sst start
is not running? (ie. sst start takes too long to startup)
m
@Nicholas Odanga you can use sam cli. You can even use sam cli to generate test events. https://docs.aws.amazon.com/cdk/latest/guide/sam.html
f
For step 6, instead of
cdk synth
run
sst build
and you can find the template yaml in
.build/cdk.out
m
You can also generate the template.yaml file with.
npx sst cdk --app=build/run.js synth --no-staging > template.yaml
f
Added it to the discussion
m
sweet! The template.yaml does include the console output so you'll want to remove it. ie.
Copy code
Preparing your SST app
Detected tsconfig.json
Transpiling source
Linting source
Running type checker
...
Might be worth add a -q flag to the cli?
n
@Frank, we are looking for a quick way to invoke our functions and mock data. I’m quite knew to the serverless world.
serverless invoke local
is very useful to quickly iterate on you code: mock the data once and with one command you can run your code multiple time.
May be a solution is to add a similar behaviour on top of
sst start
and add some utily to trigger the lambda execution with real data sent to the API Gateway. But then you have the problem that not all lambda are invoked by the API GW (see lambda and SNS for example https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html)
f
@Nicola Brisotto right on! If u r familiar with the AWS Lambda console, u can give it a mock event and invoke a function. And the mock events are saved for reuse. I’ve been thinking to bring that into
sst start
n
Thanks @Frank I had a look at the console, it works. It’s also quite easy to use the aws-cli with existing mocks
aws lambda invoke --function-name <FUNCTION_ARN> --payload "$(cat mocks/topic-create-event.json)" response.json
It’s not handy like serverless invoke (you have to find manually the lambda ARN). But it do the job 🙂
m
Doesn’t sounds like you want to use sam cli but it is good for local invoke. You can also generate events with it.
sam local generate-event [OPTIONS] COMMAND [ARGS]...
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-generate-event.html
n
Hi @Mike McCall I’m giving a try but
npx sst cdk --app=build/run.js synth --no-staging > template.yaml
produce this
template.yaml
:
Copy code
Preparing your SST app
Transpiling source
Linting source
@Frank
sst build
generate a json template how is it possible to switch to it to yaml ?
f
You can try adding the
--json
flag
n
I just tried
npx sst build --yaml
but it still generate a json
.build/cdk.out/dev-my-sst-app-my-stack.template.json
f
oh sorry mis-read ur msg.. i just looked at CDK doc, they always generate the template in JSON format
u want the yaml format b/c it’s easier to look for the lambda arn?
n
sam
require a template.yml
f
a dirty workaround for now might be
json2yaml template.json > template.yaml
? https://github.com/coolaj86/json2yaml#usage
which
sam
command r u using just curious?
n
SAM CLI, version 1.21.1
f
sorry i mean r u trying to deploy the template through
sam
?
n
invoking as @Mike McCall suggested
m
yeah, sam is kind of misunderstood I think. It can be used just for local event generation and local invoke without deploying and such
Does it ask you do specify a stack?
@Nicola Brisotto is that your entire template.yaml file? You do have to delete the sst cli console output from the top of it.
n
@Mike McCall its my entire template.yaml
I’ve just generated a new project with
npx create-serverless-stack@latest my-sst-app
m
Do you have any resources created yet?
Like a function
f
oh
cdk synth
is a bit weird, u need to give it a stack name for it to print out the template,
npx sst cdk --app=build/run.js synth dev-my-sst-app-my-stack > template.yaml
m
yup. If you do not pass a stack the output should say something like
Supply a stack id (…)
and list the stacks
n
I’ve resources created, and tried the last command by Frank but still empty.
m
did you specify the stack by the name in your app or run the exact thing he shared?
npx sst cdk --app=build/run.js ls
what is the output of that command?
n
Copy code
npx sst cdk --app=build/run.js ls
Preparing your SST app
Transpiling source
Linting source
if you want to reproduce you can try
npx create-serverless-stack@latest my-sst-app
m
sure let me try fresh
f
Chiming in again, should be
.build/run.js
instead of
build/run.js
try this
npx sst cdk --app=.build/run.js ls
n
Copy code
npx sst cdk --app=.build/run.js ls
Preparing your SST app
Transpiling source
Linting source
m
Tried on a fresh install same thing. It does work on an app generated about a month ago.
f
lemme give it a try
m
sst build runs the synth cdk synth doesn’t seem to at this time. I wonder if it has to with the --app
@Frank fwiw the app that “works” with cdk commands is 0.9.16. Prior to removing the fork.
I was able to add cdk.json file like so
Copy code
{
  "app": ".build/run.js"
}
and run any cdk command against it.
$ cdk synth
@Nicola Brisotto try
cdk synth --app=.build/run.js  --no-staging > template.yaml
n
it works on the fresh created app not my main app
f
sorry guys.. I got pulled into something at work.. I’ll have to take a look at this in an hour or so
m
hmm. I have an “old” sst app and a fresh install. cdk with
--app=.build/run.js
does appear to work on both.
@Frank hopefully this discussion has helped with some guidance! I think it might worth mentioning in the docs cdk commands work without
npx sst
. Let me know if I can help with anything.
f
@Mike McCall thanks for looking into this.. yeah I can confirm this works:
Copy code
cdk synth
And this does not work:
Copy code
npx sst cdk synth
I’m working on a fix
Will be cutting a release later tonight with the fix.
Aight this is fixed in v0.10.4, you can now run
Copy code
npx sst cdk synth
No need to pass in
--app
, that’s all hooked up internally
So @Nicola Brisotto you can run
npx sst cdk synth
and that will print out the CF template. If you have multiple stacks, pass in the stack you want printed ie.
npx sst cdk synth STACK
Let me know if it works for u.
n
thank you @Frank @Mike McCall I’ve just tested the last version. The yml out put is printed only when you have 1 stack. thank you @Frank @Mike McCall I’ve just tested the last version. The yml out put is printed only when you have 1 stack. To reproduce just start a fresh project and change
lib/index.js
Copy code
import MyStack from "./MyStack";

export default function main(app) {
  new MyStack(app, "my-stack");
  new MyStack(app, "my-stack2");  //ADD this and the yml out is gone
}
f
@Nicola Brisotto CDK synth command only prints out the template for 1 stack. you would have to pass in the name of the stack you want printed.
ie.
npx sst cdk synth STACK
n
Ok, I’ve got it
npx sst cdk synth STACK
prints out the stack only if you have NO dependency on other stacks. EX:
npx sst cdk synth dev-course-manager-sst-api
Copy code
Including dependency stacks: dev-course-manager-sst-dynamodb
Successfully synthesized to /Users/nicolabrisotto/SRC/SERVERLESS/course-manager/service/cdk.out
Another note is that the yaml I got doens’t work with
sam
I suppose because it expect a function defined as
Type: AWS::Serverless::Function
m
what exactly isn’t working?
n
sam validate
Copy code
2021-03-26 19:34:52 Loading policies from IAM...
2021-03-26 19:34:57 Finished loading policies from IAM.
Traceback (most recent call last):
  File "/usr/local/bin/sam", line 8, in <module>
    sys.exit(cli())
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/decorators.py", line 73, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/lib/telemetry/metric.py", line 152, in wrapped
    raise exception  # pylint: disable=raising-bad-type
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/lib/telemetry/metric.py", line 121, in wrapped
    return_value = func(*args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/lib/utils/version_checker.py", line 42, in wrapped
    actual_result = func(*args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/cli/main.py", line 90, in wrapper
    return func(*args, **kwargs)
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/commands/validate/validate.py", line 35, in cli
    do_cli(ctx, template_file)  # pragma: no cover
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/commands/validate/validate.py", line 55, in do_cli
    validator.is_valid()
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/commands/validate/lib/sam_template_validator.py", line 58, in is_valid
    self._replace_local_codeuri()
  File "/usr/local/Cellar/aws-sam-cli/1.21.1/libexec/lib/python3.8/site-packages/samcli/commands/validate/lib/sam_template_validator.py", line 76, in _replace_local_codeuri
    all_resources = self.sam_template.get("Resources", {})
AttributeError: 'str' object has no attribute 'get'
f
Hi @Nicola Brisotto just to clarify, you are doing this to get SAM to invoke the functions locally right?
m
It’s a moving target for sam cli to validate cdk generated templates. I have experienced the same thing. 1. run
npx sst cdk synth ReplaceMeWithStackName --no-staging -e > template.yaml
2. Delete the sst output at the top of the template ie.
Preparing your SST app…
3. Find the lambda function you want to invoke 4. Call it locally
sam local invoke ApiLambdaGetNotesFF22D02F --no-event
f
Hey @Mike McCall, apart from local invoke, do u use SAM with SST otherwise?
m
FWIW - sst has practically replaced this entire process with live lambda. Much better experience.
f
got it.. and u don’t need the CF template from SST to generate this right?
And in CI you invoke the functions locally to test? What if the functions need to call other aws services, do u mock them?
n
@Frank I’m quite new to serverless, I’m trying to understand what is the better approach. My idea at this point are: • sam is a good solution to quickly generate mock events •
aws lambda invoke --function-name <FUNCTION_ARN> --payload "$(cat mocks/topic-create-event.json)" response.json
could be useful in development but getting the list of ARN is tedious • To get the list of ARN I’ve tried this and it works
aws cloudformation list-stack-resources --region us-east-1 --stack-name dev-course-manager-sst-api | jq '.StackResourceSummaries | .[] | select(.ResourceType=="AWS::Lambda::Function") | [.LogicalResourceId, .PhysicalResourceId]'
For CI I still have to think about it, do you have any suggestion?
m
@Frank mainly creating mock events. Try to keep lambda simple business logic. Do not use local invoke in ci.
f
@Mike McCall got it! Something i’ve been thinking is letting ppl save a request event when running
sst start
, and u can invoke with them later.
@Nicola Brisotto Can you show me ur SST code? I might be have an easier way for u to get the the function ARNs.
n
Hi @Frank at the moment is a simple app with DynamoDB and an a few rest APIs (at the moment only one in SST because we are just testing it). lib/ApiStack.js
Copy code
import * as cdk from "@aws-cdk/core";
import * as sst from "@serverless-stack/resources";
import * as apig from "@aws-cdk/aws-apigatewayv2";

export default class MyStack extends sst.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    const { tableName } = props;

    // Create the HTTP API
    const api = new sst.Api(this, "Api", {
      defaultFunctionProps: {
        environment: { appTable: tableName },
      },
      cors: {
        allowMethods: [apig.HttpMethod.GET],
      },
      routes: {
        "POST /topics": "crud/create.main",
      },
    });

    // Show API endpoint in output
    new cdk.CfnOutput(this, "ApiEndpoint", {
      value: api.httpApi.apiEndpoint,
    });
  }
}
lib/index.js
Copy code
import S3Stack from "./S3Stack";
import CognitoStack from "./CognitoStack";
import DynamoDBStack from "./DynamoDBStack";
import ApiStack from "./ApiStack";

// Add stacks
export default function main(app) {
  const dynamo = new DynamoDBStack(app, "dynamodb");

  const s3 = new S3Stack(app, "s3");

  new CognitoStack(app, "cognito", { bucketArn: s3.bucket.bucketArn });

  new ApiStack(app, "api", {tableName: dynamo.appTable.tableName});

}
f
Add this to the bottom of ur ApiStack:
Copy code
new cdk.CfnOutput(this, "POST_topics_LambdaArn", {
      value: api.getFunction("POST /topics").functionArn,
    });
It will print out the Lambda function’s ARN after the deploy
Here’s a short hand syntax to replace the two CfnOutputs:
Copy code
this.addOutputs({
  ApiEndpoint: api.httpApi.apiEndpoint,
  POST_topics_LambdaArn: api.getFunction("POST /topics").functionArn,
});
n
Thank you @Frank! Would be nice to have a way to iterate on all routes, so it will be easy to add all of them as outputs.
f
Oh yeah I can see that!