Getting Table Records from the API in JavaScript

Hey All,

Wanted to share something that I struggled with some. I wanted to make a different table viewer, but this meant I needed to be able to hit the tables api to get all of its records. Here is a function that does just that. Two things you need to change:

  1. Create a bot in your instance, and copy its basic auth code to the authorizationBasic variable.
  2. Update the URL you want to pull data from inside the request object.
async function callAPI() {
  console.log("in fetch machine data")
  const authorizationBasic =
    "Basic YXB...NmlXag==";

  let myHeaders = new Headers();
  myHeaders.append("Accept", "application/json");
  myHeaders.append("Authorization", authorizationBasic);
  myHeaders.append(
    "Content-Type",
    "application/x-www-form-urlencoded; charset=UTF-8"
  );

  const myInit = {
    method: "GET",
    headers: myHeaders,
    mode: "cors",
    cache: "default"
  };

  let myRequest = new Request(
    `https://petehartnett.tulip.co/api/v3/tables/RZdpY9NwDMPskoEE5/records?limit=100`
  );

  const response = await fetch(myRequest, myInit);
  const machineData = await response.json();
  console.log(response.status);
  //console.log(machineData);
  return machineData;
}

Some resources if you have never worked with the api:

Let me know if you run into any issues as we can work through them as a team-
Pete

Pete,

I just wrote a feature request that’s related to this. This approach you outlined here is an okay workaround, but there’s a few things that bother me.

  1. The Tulip front-end already has access to the Table API and credentials. To get those credentials, the user might even have had to have used 2FA.
  2. I don’t know if the credentials the UI uses are temporary or not (I hope so) but if they’re tied to the user’s login, then they are also tied to the client’s security policy e.g. maybe SAML2/LDAP, maybe requiring 2FA
  3. You can’t really rotate bot credentials without having to go touch every widget, or, alternatively, make the credentials an input to the widget. Neither of those things are great.
  4. Once you give the widget bot credentials, those credentials have access to every table, which just increases the vulnerability footprint

what I am suggesting instead is that we make the ability for custom widgets be a first-class citizen. Hand the widget a table reference, give it get() create() update() delete() methods associated with the specific tables passed into the reference, and have those methods automatically inject the same credentials the UI user is already using.

Along the same lines, step 2 would be pass in a reference to the App with a few methods like .complete() and .goToNamedStep(“something”).

Probably should discuss this in my feature request thread rather than here.

1 Like

Hi Pete,

if I try call the async function from within the custom widget I get a CORS error.

Any help would be appreciated thank you.

Hey @John_L -

These are the functions I reuse everytime I do this

async function callAPI() {
  //console.log("in fetch machine data")
  const authorizationBasic =
    "YXBpa2V....==";

  let myHeaders = new Headers();
  myHeaders.append("Accept", "application/json");
  myHeaders.append("Authorization", "Basic " + authorizationBasic);
  myHeaders.append(
    "Content-Type",
    "application/x-www-form-urlencoded; charset=UTF-8"
  );

  const myInit = {
    method: "GET",
    headers: myHeaders,
    mode: "cors",
    cache: "default"
  };

  let myRequest = new Request(
    `https://petehartnett.tulip.co/api/v3/tables/RZdpY9NwDMPskoEE5/records?limit=100`
  );

  const response = await fetch(myRequest, myInit);
  const data = await response.json();
  return data;
}

async function displayData(){
    let data = await callAPI();

}

Pete

Hi Pete, thank you for the example…

  • How do you call your displayData() method button click event?

  • Did you need to enable any sort of CORS setting on the tulip server instance?

  • Is it possible to get the machine activity data by making an api call from inside the widget?

Kind Regards.

Thank you Pete for this piece of code, it’s very useful and works for me.
However, I’m trying to take it a step further and use a POST method to create an entry in my table, for starters at a Dummy table I’ve created for that purpose.
I’m using the following code and it gets stuck (seems like an enless loop) when trying to receive the response:

async function callAPI() {
  console.log("in callAPI")
  const authorizationBasic =
    "Basic YXB...==";

  let myHeaders = new Headers();
  myHeaders.append("Accept", "application/json");
  myHeaders.append("Authorization", authorizationBasic);
  myHeaders.append("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

  const myInit = {
    method: "POST",
    body: JSON.stringify({
        "id": "4",
        "Name": "I did it myself",
        "Num": 100.0
    }),
    headers: myHeaders,
    mode: "cors",
    cache: "default"
  };

  let myRequest = new Request(
    `https://...tulip.co/api/v3/tables/.../records`
  );
  console.log("in callAPI, created objects")

  const response = await fetch(myRequest, myInit);
  console.log("in callAPI, after fetch")
  const dummies = await response.json();
  console.log("in callAPI, after response")
  console.log("respose: " + response.status);
  console.log(dummies);
  return dummies;
}

callAPI();

BTW, if I remark the body: part in myInit it works correctly and I get the 422 error code:
{“details”:“You have provided URL query parameters, but none are expected. Did you mean to provide a JSON body instead?”,“errorCode”:“NoQueryParamsExpected”}

What needs changing in order for the above to work?
Thank you!

@AquaMaofian I got the post to work

headers look like this…
let myHeaders = new Headers();
myHeaders.append(“Accept”, “application/json”);
myHeaders.append(“Authorization”, "Basic " + authorizationBasic);

to call the “callAPI” function do this

async function doApiCall(){
let data = await callAPI();
console.log(data);
}

doApiCall();

If you getting a 422 it probably means that the record with that id already exists.
Hope this helps.

Thank you John for your answer.
However, when I run it (after I’ve added the following):
"
async function doApiCall(){
let data = await callAPI();
console.log(data);
}

doApiCall();
"
The result was the same, still not getting to the console.log(“in callAPI, after response”) part.

Side note: I’ve also tried importing code snippets from other sources. They are very similar to what we have above and similarly don’t work.

Will it be possible to answer with a POST code snippet that works for you?

const myInit = {
method: “POST”,
body: JSON.stringify({
“id”: “myIdentifier”
}),
headers: myHeaders,
mode: “cors”,
cache: “default”
};

let myRequest = new Request(
https://.....
);

const response = await fetch(myRequest, myInit);
const data = await response.json();
return data;

I’m not sure what the problem with my instance (I’m still on a trial one, is that an issue?), but I can’t make sense in why my code doesn’t work. What I’ve tried:

  1. I removed the
    body: JSON.stringify({
    “id”: “myIdentifier”
    }),

    part and get an error as expected.
  2. I changed it to a GET and removed the above body (everything else stays the same) and it’s working fine (I get all the entries of my table.
  3. I’ve ran a POST request with the same bot credentials through Insomnia and it works (an new entry is properly created).
  4. I’ve tried another POST request, to create a new table - it fails in exactly the same manner (again, in Insomnia it’s executed correctly, as expected).
    And (5) - John reports that it runs just fine for him - can someone help me to clarify and resolve the issue?
    Thanks a bunch!

Can you provide details of the error you are getting?

Hi John, please see my response to Pete from 4 days ago above: I’m using the following code and it gets stuck when trying to receive the response.
Running the above code I get the responses:
in callAPI
in callAPI, created objects
in callAPI, after fetch
But nothing else is coming (e.g. in callAPI, after response), although I’ve waited, re-run it many times and refreshed the page.

Can you see the network request when you open dev tools in the brower? Can you share if you are getting any error response at all when the request is made?

I apologize, I’m not sure what you mean by “dev tools in the brower” - I’m very much in the dark regarding debugging the javascript coce I’m writing in the custom widget - is there a way to see more under the hood? Please send a link.
As I’ve said before, I get no error (unless I mess the code, as I’ve detailed in my last reply). The attached is a what I get when I refresh the control.


As you can see, it never reaches the console.log(“in callAPI, after response”); log line.

And the table doesn’t get the new entry:

image

If you open the developer tools in the browser and click on network and make sure the preserve log button is checked, you should be able to see the network call that was made and hopefully that will help, sorry but that is as much help as I can offer, good luck.