Tom Parke
Tom Parke

Reputation: 119

How do I prevent API returning exponentially increasing escaped strings?

I have recently come across a problem I have never seen or even heard of before. I am using an AWS HTTP API Gateway which is integrated with a simple lambda function that returns a response from a DB. Hardly rocket surgery. The problem is this; when I make a request the first time, the response comes back as it should, with the body as JSON.

enter image description here

When I make an identical request a few seconds later, the response looks like this:

enter image description here

And the third time, this:

enter image description here

It gets worse and worse each time. My function response follows the rules defined in the developer guide, and as you can see from the first response, it's working just fine.

Why it would escape multiple times after that is beyond me. In my lambda code, I stringify the JSON once response.body = JSON.stringify(response.body) to follow the spec. There is nothing in my code that even knows to stringify multiple times based on an individual users IP address only after a certain number of requests within a certain timespan that go back to normal after a half hour or so.

Any help would be appreciated.

UPDATE:

index.js

const users = require ('./handlers/users.js')
const auth = require ('./lib/auth.js')

exports.handler = async (event) => {

  const body = event.body ? JSON.parse(event.body) : undefined;
  const queryString = event.queryStringParameters
  const authHead = event.headers.authorization ? JSON.parse(event.headers.authorization) : undefined
  
  const path = event.requestContext.http.path
  const method = event.requestContext.http.method

  const authUser = auth(authHead)

 // I have more handlers, but you get the idea...

  const handlers = {
    '/users': {
      POST: users.post,
      PATCH: users.patch,
      GET: users.get,
    },
  }

  const response = await handlers[path][method](authUser, queryString, body)
  response.body = JSON.stringify(response.body)

  return response;
};

/handlers/users.js

const AWS = require('aws-sdk')
const docClient = new AWS.DynamoDB.DocumentClient()

const response = {
  statusCode: 500,
  body: {
    message: 'unhandled error',
    payload: undefined
  }
}

exports.get = async (authUser, queryString, body) => {


  const params = {
    TableName: 'db-name',
    Key: {
      'PK': undefined,
      'SK': undefined
    }
  }

  if (queryString.id) {
    params.Key.PK = queryString.id;
    params.Key.SK = `user#${queryString.id}`;
  } else if (queryString.username) {
    params.IndexName = 'GSI'
    params.ExpressionAttributeValues[':ID'] = queryString.username
  } else {
    response.statusCode = 400
    response.body = { message: 'bad request', payload: undefined }
    return response
  }

  try {
    const results = await docClient.get(params).promise()
    response.body.payload = results
    response.statusCode = 200
    response.body.message = 'success'
    return response
  }
  catch (err) {
    console.log(err)
    response.statusCode = 400
    response.body = 'failed'
    return response
  }

}

Upvotes: 2

Views: 546

Answers (1)

user202729
user202729

Reputation: 3965

You defined response as a const global object:

const response = {
  statusCode: 500,
  body: {
    message: 'unhandled error',
    payload: undefined
  }
}

Because in JavaScript, assignment is not object copy, and const object can be modified, when you modify response.body, you're actually modifying the body property of the global response object.

To fix the issue: just move the const response = ... assignment to inside the async (authUser, queryString, body) => { ... } function. It will create a new object each time.

Upvotes: 2

Related Questions