appsmith: [Bug]: JSON response gets stringified by server

Is there an existing issue for this?

  • I have searched the existing issues

Description

Slack Conversation

Error in JSON format returned by users’ API server gets stringified by Appsmith server.

Steps To Reproduce

  1. Create a REST API e.g. Api1
  2. Enter https://mock.codes/500 in the URI
  3. Drag and drop a text widget and use {{Api1.data.description}} in the Text field

You will notice that the evaluated value doesn’t show anything because the response is coming as stringified.

Public Sample App

No response

Environment

Production

Issue video log

No response

Version

Cloud / Self Hosted

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 15 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Can’t explain this. Really.

Basically, all of my bindings and functions were working until last update (1.9.15). I checked the doc (https://docs.appsmith.com/reference/datasources/airtable) and seems that no JSON.parse is involved in the binding example:

// binding response data to a Table widget
{{
  AirtableQuery.data.records.map(row => {
    return {
      "Name": row.fields.name,
      "Employee ID": row.fields.employee_id
    }
  })
}}

So, no clue! I don’t have any valid explanation about how my code used to work until 1.9.15 (but I’m sure it did, since I had many end users using my app).

The bottom line is that It’s not a big deal to update my app code to JSON.parse .data - as long as this won’t change back in the future.

As a side consideration, under a UX perspective, using JSON.parse in every binding dialogue could be sub-optimal in terms of clarity (and probably not immediate for most users). I only use airtable as a datasource, so I don’t know if parsing is needed when binding using other integrations. In this case, consistency would be the best design choice.

Thank you for tackling this issue, I’m available for testing if you need!

Update

The fix is now available on Appsmith cloud i.e. app.appsmith.com with Appsmith Version: v1.9.17.

cc: @lelesrc

@lelesrc Thanks a million for doing end-to-end testing on this. I really appreciate your effort. 🙇‍♂️

I put up the simplest test case I came up with on ce-22498.dp.appsmith.com:

  1. created a datasource
  2. created a testReturnObject query retrieving a single record by id
  3. created a text field and binded it to {{testReturnObject.data.id}}

It works as expected, the record id is printed. The data object is an object. I also tested the output of testReturnObject.run() in a JSObject:

export default {
	getId: async () => {
		let data = await testReturnObject.run()
		return data 
	}
}

data returned by the getId function is an object.

As a counter-example, I did the same on 1.9.15 and testReturnObject.data.id is undefined. The data object is a stringified JSON. The same with the getId function using testReturnObject.run(): the result is a string.

Happy to help!

@lelesrc Thank you very much for your valuable input. We don’t have any clue either 😕 Seems really weird.

However, while we are trying to come up with the best solution can you please check whether the Airtable query result is giving the expected output in the following deploy-preview?

https://ce-22498.dp.appsmith.com (Note: You need to sign up)

Would really appreciate your feedback!

So many users have already reported their apps aren’t working as expected. Either they need to use JSON.parse in all the occurrences or they have to wait for a fix.

Considering the above scenario I have added the critical label.

Issue with Airtable

The data returned by Airtable is in valid JSON format but after deserialization happens in Appsmith the body in ActionExecutionResult contains stringified JSON.

cURL request shows that the response is in valid JSON format

curl --location 'https://api.airtable.com/v0/{baseID}/{tableName}' \
--header 'Authorization: Bearer API_KEY'

{
  "records": [
    {
      "id": "rec3dVlLB33uR0PEE",
      "createdTime": "2022-08-08T10:54:29.000Z",
      "fields": {
        "Name": "test patch",
        "Status": "Todo",
        "Notes": "nice"
      }
    },
    {
      "id": "rec7ZO0YX4wdQRt8Z",
      "createdTime": "2022-08-16T09:45:20.000Z",
      "fields": {
        "Name": "latest id ",
        "Status": "Todo",
        "Notes": "PRAPULLA12@#$%"
      }
    },
    {
      "id": "recB4rdvSYFrIWFsp",
      "createdTime": "2021-11-09T09:07:18.000Z",
      "fields": {
        "Name": "test patch",
        "Status": "Todo",
        "Notes": "nice"
      }
    },
    {
      "id": "recGuJsxd69IeUQ8g",
      "createdTime": "2022-08-08T10:53:09.000Z",
      "fields": {
        "Name": "test patch",
        "Status": "Todo",
        "Notes": "nice"
      }
    },
    {
      "id": "recMSLsSWhz6XLeYa",
      "createdTime": "2021-11-09T09:22:52.000Z",
      "fields": {
        "Name": "Closed",
        "Status": "In progress",
        "Notes": "hi"
      }
    },
    {
      "id": "recZIfsrHIAIEvHkD",
      "createdTime": "2021-11-09T09:07:18.000Z",
      "fields": {}
    },
    {
      "id": "recdfdrpukvoALby9",
      "createdTime": "2021-11-26T06:11:35.000Z",
      "fields": {
        "Name": "test",
        "Notes": "hi"
      }
    },
    {
      "id": "recjBOC8Scg4vTLgo",
      "createdTime": "2021-11-26T06:11:35.000Z",
      "fields": {
        "Name": "test",
        "Notes": "hi"
      }
    },
    {
      "id": "reckB49idYGo3hDdP",
      "createdTime": "2021-11-09T10:29:35.000Z",
      "fields": {
        "Name": "test",
        "Status": "Todo",
        "Notes": "hi"
      }
    },
    {
      "id": "recrPq8EST7K0Soo5",
      "createdTime": "2021-11-09T10:29:35.000Z",
      "fields": {
        "Name": "test",
        "Status": "Done",
        "Notes": "hi"
      }
    },
    {
      "id": "recraPx0luOphnfgM",
      "createdTime": "2021-11-09T09:22:52.000Z",
      "fields": {
        "Name": "Closed",
        "Status": "Done",
        "Notes": "hi"
      }
    }
  ]
}

Solution

The first deserialization is done by this piece of code

ActionExecutionResult result = saasObjectMapper.readValue(body, ActionExecutionResult.class);

Airtable query results can be found as stringified JSON in the body (type object) field of ActionExecutionResult object. The data type of the body field is checked with String and it’s a string type that might contain stringified JSON. Later, the body is attempted to convert to a JSON node. In case it failed with a JSON processing error, string type is retained as the fallback type.