Add a Get Note API

@jayair I’m not sure on this point. As my understanding, if we call callback(error) in our lambda functions whenever an error occurred, the error will be notified to AWS service like CloudWatch for logging/monitoring purpose. However, the updated version of this chapter use return instead of callback() function.

Reference: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-mode-exceptions.html

@jayair in the list-event.json file for the mock input used to test the new list service locally, @starkos and I had to use “body”: “{“text”:”"}" as an attribute in the list-event.json file to get the test to work.

My question is why do we have to use it here and not in other areas?

Thanks!

You can use the callback(error) if you’d like. But we didn’t use it in the past since we are essentially handling the error ourself and we want to control which headers we want to send out. There is a way to tell API Gateway which headers to use in the case of a Lambda exception but it is a bit more complicated.

Prior to this return change, we were simply doing callback(null, response). That is exactly the same as the return response. The way we would send it to CloudWatch would be to console.log(error). Hope that makes sense.

1 Like

Hmm usually you need the body if it is not a GET request. Can you confirm that it is a GET request?

I am also getting the “Item not found” error, while I can get a record query dynamoDB directly using the same userId and noteId in the get-event.json. Do you have suggestion where may be wrong?

Thanks!

No worries, I had a typo, it works now!

Thanks!

1 Like

I’m a relative noob on all this (aws, serverless), but this might be one of the absolute best tutorials i ever came across. On any topic that is. I made heaps of progress on aws, react, bootstrap, and serverless with this.

Me and my bud have been trying to set up a serverless backend with both a web frontend, and an iOS native swift client.
The latter would have to use a swift aws api-gateway sdk which is generated from the aws console. But it seems that this function is unaware of the path parameter (being {id} in this case) the API endpoint needs. So for example, when i generate the SDK for the notes app in this tutorial after deploying the functions to aws with serverless deploy, the getNote function in the generated swift sdk doesn’t take an id parameter.
Turns out path parameters have to be explicitely defined in serverless.yml for the SDK functions to be properly generated, including the path parameters as its function parameters. This might be a new addition since this tutorial was written, and if it is i’m sorry. But the function setup in serverless.yml for iOS SDK generation to work properly would be as follows:

 - http:
          path: notes/{id}
          method: get
          cors: true
          authorizer: aws_iam
          request:
                 parameters:
                        paths:
                               id: true

So the request part and below are new, they define the path parameters. For interaction with a react frontend, obviously explicitely defining this doesn’t seem to be necessary, but for the iOS SDK turns out it is. Oh well, that’s AWS for ya!

1 Like

I got this issue on running get-event.json. Could anyone find out what is this?

    $ serverless invoke local --function get --path mocks/get-event.json
 
  Serverless Error ---------------------------------------
 
  Events for "get" must be an array, not an object

Hey guys, sorry I got the solution for my above mentioned issued. It is because of the space between - and http in serverless.yml

get:
    handler: get.main
    events: 
      - http:
          path: notes/{id}
          method: get
          cors: true
          authorizer: aws_iam
1 Like

Thanks for the kind words!

And thanks for the feedback. I’ll add a note to the chapter with this!

Glad you figured it out! Thanks for reporting back.

I am getting the following error. I have checked and checked I cannot see what I did wrong. I can create an event just cant get it.

C:\Users\activated\myfitnessbook-app-api>serverless invoke local --function create --path mocks\get-event.json
Serverless: Bundling with Webpack...
Time: 620ms
Built at: 07/07/2019 9:04:46 PM
        Asset      Size  Chunks             Chunk Names
    create.js  10.1 KiB  create  [emitted]  create
create.js.map  7.23 KiB  create  [emitted]  create
Entrypoint create = create.js create.js.map
[./create.js] 2.37 KiB {create} [built]
[./libs/dynamodb-lib.js] 468 bytes {create} [built]
[./libs/response-lib.js] 762 bytes {create} [built]
[aws-sdk] external "aws-sdk" 42 bytes {create} [built]
[babel-runtime/core-js/json/stringify] external "babel-runtime/core-js/json/stringify" 42 bytes {create} [built]
[babel-runtime/helpers/asyncToGenerator] external "babel-runtime/helpers/asyncToGenerator" 42 bytes {create} [built]
[babel-runtime/regenerator] external "babel-runtime/regenerator" 42 bytes {create} [built]
[source-map-support/register] external "source-map-support/register" 42 bytes {create} [built]
[uuid] external "uuid" 42 bytes {create} [built]
{
    "errorMessage": "Unexpected token u in JSON at position 0",
    "errorType": "SyntaxError",
    "stackTrace": [
        "SyntaxError: Unexpected token u in JSON at position 0",
        "    at JSON.parse (<anonymous>)",
        "    at _callee$ (C:\\Users\\activated\\myfitnessbook-app-api\\.webpack\\service\\webpack:\\create.js:6:21)",
        "    at tryCatch (C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\regenerator-runtime\\runtime.js:62:40)",
        "    at Generator.invoke [as _invoke] (C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\regenerator-runtime\\runtime.js:296:22)",
        "    at Generator.prototype.(anonymous function) [as next] (C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\regenerator-runtime\\runtime.js:114:21)",
        "    at step (C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\babel-runtime\\helpers\\asyncToGenerator.js:17:30)",
        "    at C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\babel-runtime\\helpers\\asyncToGenerator.js:35:14",
        "    at new Promise (<anonymous>)",
        "    at new F (C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\core-js\\library\\modules\\_export.js:35:28)",
        "    at C:\\Users\\activated\\myfitnessbook-app-api\\node_modules\\babel-runtime\\helpers\\asyncToGenerator.js:14:12",
        "    at main (C:\\Users\\activated\\myfitnessbook-app-api\\.webpack\\service\\create.js:153:17)",
        "    at BbPromise (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\lib\\plugins\\aws\\invokeLocal\\index.js:628:30)",
        "    at Promise._execute (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\debuggability.js:313:9)",
        "    at Promise._resolveFromExecutor (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\promise.js:488:18)",
        "    at new Promise (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\promise.js:79:10)",
        "    at AwsInvokeLocal.invokeLocalNodeJs (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\lib\\plugins\\aws\\invokeLocal\\index.js:582:12)",
        "    at AwsInvokeLocal.invokeLocal (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\lib\\plugins\\aws\\invokeLocal\\index.js:151:19)",
        "    at AwsInvokeLocal.tryCatcher (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\util.js:16:23)",
        "    at Promise._settlePromiseFromHandler (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\promise.js:517:31)",
        "    at Promise._settlePromise (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\promise.js:574:18)",
        "    at Promise._settlePromiseCtx (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\promise.js:611:10)",
        "    at _drainQueueStep (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\async.js:142:12)",
        "    at _drainQueue (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\async.js:131:9)",
        "    at Async._drainQueues (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\async.js:147:5)",
        "    at Immediate.Async.drainQueues (C:\\Users\\activated\\AppData\\Roaming\\npm\\node_modules\\serverless\\node_modules\\bluebird\\js\\release\\async.js:17:14)",
        "    at runCallback (timers.js:705:18)",
        "    at tryOnImmediate (timers.js:676:5)",
        "    at processImmediate (timers.js:658:5)",
        "    at process.topLevelDomainCallback (domain.js:126:23)"
    ]
}

C:\Users\activated\myfitnessbook-app-api>

That’s a really strange error. It’s pointing to create.js:6:21. Does that show you anything?

I figured it out the issue wasn’t on create.js but on mocks get-event.json. In the documentation there isnt a “body” block so that is why it was showing the error.
Here is the original in the document

{
  "pathParameters": {
    "id": "578eb840-f70f-11e6-9d1a-1359b3b22944"
  },
  "requestContext": {
    "identity": {
      "cognitoIdentityId": "USER-SUB-1234"
    }
  }
}

Here is my edited one which worked for me.

{
  "pathParameters": {
    "id": "802ee100-a0ed-11e9-aae8-112e35b3572a"
  },
  "body": "{\"text\":\"\"}",
  "requestContext": {
    "identity": {
      "cognitoIdentityId": "USER-SUB-1234"
    }
  }
}
1 Like

Glad you figured it out.

I followed the guide to get the note created in the previous chapter.

My create function is working but the get is always returning item not found. I have created three notes so far and but get returns item not found for all three note IDs.

get-event.json

{
  "pathParameters": {
    "id": "0ac939d0-bd6f-11e9-83ec-233c3af4d792"
  },
  "requestContext": {
    "identity": {
      "cognitoIdentityId": "USER-SUB-1234"
    }
  }
}

Made a very silly mistake! in the try block inside get, I used result.item instead of result.Item. It works now! Can’t wait to continue with the rest of the guide :slight_smile:

1 Like

Two calls observed in Chrome dev-tools network explorer.

Wondered if anybody else has come across this.

When I make a call to (my equivalent of) the get Notes API I see two calls in my network explorer (Crome dev tools). (I see this on all API calls)
The fist one has an empty response but with some response headers. The second returns the data. The first call can take up to 800ms. The second 1.64 seconds.

I suspect the first call is a response from API gateway?

My question - can someone confirm if they also see two calls? And secondly - how do I get around it - if possible. It is a bit of a performance issue.

Regards

It’s quite likely the first one is the CORS request. Is it an OPTIONS request?

Yes. it is an OPTIONS request.
The only way I managed to get around it is by setting the maxAge on the headers coming back. The entry below in serverless.yml will ensure that every api call doesn’t need the OPTIONS call- only the first or if the maxAge has expired.

The second part (new) of the issue is that on the first call there is a call to the cognito service. https://cognito-identity … This is not initiated by the
await Auth.currentSession(); which I initially thought. This “I think” will use the cache to check if the current session is valid and refresh it in need.

It is initiated by the first call to the method below. (Subsequent calls do not invoke the cognito call). Any ideas on that?

To set maxAge on api method.

list:
# Defines an HTTP API endpoint that calls the main function in list.js
# - method: GET request
handler: list.main
events:
- http:
path: yourPath
method: get
cors:
origin: ‘*’
maxAge: 86400
headers:
- Content-Type
- X-Amz-Date
- Authorization
- X-Api-Key
- X-Amz-Security-Token
- X-Amz-User-Agent
allowCredentials: false
cacheControl: ‘max-age=600, s-maxage=600, proxy-revalidate’
authorizer: aws_iam