amplify-js: Amplify API get request example for listing all items throws an error

I followed all the steps in the official doc and when used the code in the example as below replacing the API name and path, it doesn’t return the list, instead it throws 404 error. I haven’t made any manual changes in the API or related Lambda function.

async getData() { 
    let apiName = 'MyApiName';
    let path = '/path';
    let myInit = { // OPTIONAL
        headers: {} // OPTIONAL
    }
    return await API.get(apiName, path, myInit);
}

getData();

But, if I pass an id of an existing record with the same request, it returns the object.

Ex.

async getData() { 
    let apiName = 'MyApiName';
    let path = '/path/<id>';
    let myInit = { // OPTIONAL
        headers: {} // OPTIONAL
    }
    return await API.get(apiName, path, myInit);
}

getData();

Is there a different way of loading all items?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 22 (1 by maintainers)

Most upvoted comments

Actually, I found out that it is required to add my own get method to scan the database. Added the following function in the app.js file of the Lambda Function. IT IS REQUIRED TO ADD THIS FUNCTION ABOVE OTHER get FUNCTIONS.

app.get('/<YOUR_PATH>', function (req, res) {
  var params = {
    TableName: tableName,
    Select: 'ALL_ATTRIBUTES',
  };
  dynamodb.scan(params, (err, data) => {
    if (err) {
      res.json({ error: 'Could not load items: ' + err.message });
    }
    res.json({
      data: data.Items.map(item => {
        return item;
      })
    });
  });

Why is the issue is closed. The behavior is still present. I can’t get a collection of items without the fix which is described in this thread.

This is still an issue 😃 Can we reopen this please?

You are missing a closing }); but this is indeed needed!

Emphasis that @sam-webchef’s example is missing the closing }); ❗️

I was troubleshooting this in CloudWatch logs and found Runtime.UserCodeSyntaxError: SyntaxError: Unexpected end of input" on my recent lambda requests.

For completeness:

I had the same Issue where adding a REST API with Amplify’s amplify add api command generated the /amplifiy/backend/function/myApi/src/app.js file and used the default path of /items. Then in my app, API.get('myApi', '/items', {}); resulted in Uncaught (in promise) Error: Request failed with status code 404 in the browser’s Javascript console.

I added the following block of code ABOVE the first app.get() function, at approximately line 68 in /amplify/backend/function/myApi/src/app.js:

/********************************
 * MANUALLY ADDED
 * HTTP Get method to scan all objects *
 *
 * The generated lambda code from `amplify add api` only created a `get` endpoint
 * to "list" an _individual_ object.
 *
 * This adds the ability to perform a scan which returns an Items array of all objects.
 ********************************/

app.get(path, function (req, res) {
  var params = {
    TableName: tableName,
    Select: 'ALL_ATTRIBUTES',
  };

  dynamodb.scan(params, (err, data) => {
    if (err) {
      res.json({ error: 'Could not load items: ' + err.message });
    }

    res.json({
      data: data.Items.map(item => {
        return item;
      }),
    });
  });
});

Then run amplify push and observe that your API Function is flagged to Update. Hit Y + enter to deploy the new function you just modified.

If that succeeds, the request to API.get('myApi', '/items', {}); should now succeed as a 200 response and include data in the following structure (the items in your data array will be your structure):

{
  "data": [
    {
      "id": "abc123",
      "name": "test"
    }
  ]
}

To confirm this was working end-to-end, I also used the generated API to create one item in the database like so:

  // In App.js / App.tsx
  async function createItem(item: any) {
    const params = {
      body: { ...item }
    }
    const response = await API.post('myAPI', '/items', params);
    console.log('-- createItem() response', response);
  }

  createItem({ id: 'abc123', name: 'test' });

It would be really nice to see the REST configuration of amplify add api generate a app.get() method to list all objects (perhaps, with optional pagination?). It’s a very common use case.

Actually, I found out that it is required to add my own get method to scan the database. Added the following function in the app.js file of the Lambda Function. IT IS REQUIRED TO ADD THIS FUNCTION ABOVE OTHER get FUNCTIONS.

app.get('/<YOUR_PATH>', function (req, res) {
  var params = {
    TableName: tableName,
    Select: 'ALL_ATTRIBUTES',
  };
  dynamodb.scan(params, (err, data) => {
    if (err) {
      res.json({ error: 'Could not load items: ' + err.message });
    }
    res.json({
      data: data.Items.map(item => {
        return item;
      })
    });
  });

You are missing a closing }); but this is indeed needed!

Although I don’t understand why as the out-of-the-box Lambda function says it has already such function? (which indeed doesn’t work as described)

/********************************
 * HTTP Get method for list objects *
 ********************************/

app.get(path + hashKeyPath, function(req, res) {
  var condition = {}
  condition[partitionKeyName] = {
    ComparisonOperator: 'EQ'
  }

  if (userIdPresent && req.apiGateway) {
    condition[partitionKeyName]['AttributeValueList'] = [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH ];
  } else {
    try {
      condition[partitionKeyName]['AttributeValueList'] = [ convertUrlType(req.params[partitionKeyName], partitionKeyType) ];
    } catch(err) {
      res.json({error: 'Wrong column type ' + err});
    }
  }

actually the code

` /********************************

  • HTTP Get method for list objects * ********************************/

app.get(path + hashKeyPath, function (req, res) { var condition = {} condition[partitionKeyName] = { ComparisonOperator: ‘EQ’ }

if (userIdPresent && req.apiGateway) { condition[partitionKeyName][‘AttributeValueList’] = [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH] } else { try { condition[partitionKeyName][‘AttributeValueList’] = [convertUrlType(req.params[partitionKeyName], partitionKeyType)] } catch (err) { res.statusCode = 500 res.json({ error: 'Wrong column type ’ + err }) } }

let queryParams = { TableName: tableName, KeyConditions: condition }

dynamodb.query(queryParams, (err, data) => { if (err) { res.statusCode = 500 res.json({ error: 'Could not load items: ’ + err }) } else { res.json(data.Items) } }) }) `

is working but if you see is the path + hashKeyPath and

const hashKeyPath = '/:' + partitionKeyName;

so if you do this

API.get("rest" , "/items/:");

will work without adding some extra code

I have a similar problem. Seems like the DynamoDB scanning already implemented in the template generated by create API

/********************************
 * HTTP Get method for list objects *
 ********************************/

app.get(path + hashKeyPath, function (req, res) {
  var condition = {}
  condition[partitionKeyName] = {
    ComparisonOperator: 'EQ'
  }

  if (userIdPresent && req.apiGateway) {
    condition[partitionKeyName]['AttributeValueList'] =
      [req.apiGateway.event.requestContext.identity.cognitoIdentityId || UNAUTH]
  } else {
    try {
      condition[partitionKeyName]['AttributeValueList'] =
        [convertUrlType(req.params[partitionKeyName], partitionKeyType)]
    } catch (err) {
      res.statusCode = 500
      res.json({ error: 'Wrong column type ' + err })
    }
  }

  let queryParams = {
    TableName: tableName,
    KeyConditions: condition
  }

  dynamodb.query(queryParams, (err, data) => {
    if (err) {
      res.statusCode = 500
      res.json({ error: 'Could not load items: ' + err })
    } else {
      res.json(data.Items)
    }
  })
})

I do const data = await API.get('picturesapi', '/userpictures') from the client and get 404.