Tip: Turning a normal async function that works elsewhere into a context.async function that works into Bubble plugin actions

Hello! Since I have a few minutes until my next pomodoro, I decided to share this tip.

In case you make or find an asynchronous function working at node or browser and want to convert it into a context.async function… here’s an example!

Before

// declaring it
const requestToGetPlainText = async (fileUrl) => {

  let response = await fetch(fileUrl);
  let plainText = await response.text();
  return plainText

}

// calling it
requestToGetPlainText("https://example.com/myFile.csv")

After

// this declares AND calls it at the same time
let attainedPlainText = context.async(async callback => {
  try {
    let response = await fetch(properties.file_url);
    let plainText = await response.text();
    callback(null, plainText);
  }
  catch (err) {
    callback(err);
  }
});

// this returns it from server side 
// but you could publish attainedPlainText to 
// a custom element state if client side
return {
  attained_plain_text: attainedPlainText
}

Case you’re wondering how to use fetch within node, just grab this node package:
const fetch = require('cross-fetch');

Happy Bubbling!

22 Likes

@vini_brito I’ve been searching EVERYWHERE for this. Quick question, prefaced with “I barely know what I’m doing in Node”:

I’m using the node-fetch module to make a POST request (which seems like cross-fetch uses under the hood). All works great when tested in repl.it. However, when I make the call through a server-side action in Bubble, I get a socket-hangup every time.

I have a feeling it has to do with how Bubble handles the call – do I need to refactor to use context.async when using fetch?

function(properties, context) {

const fetch = require('node-fetch')
var data

const queryThis = {
  query: "{ shop {  name primaryDomain {  url host }}}",
}

fetch(`https://shopname.myshopify.com/api/2019-07/graphql`, {
  method: 'POST',
  body: JSON.stringify(queryThis),
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-Shopify-Storefront-Access-Token': 'myshopToken',
  }
}).then(res => res.text())
  .then(body => {
    console.log(body)
    data = body
    console.log("hello!!!!!!!!!!!!!!")
  }
  ) 
  .catch(error => console.error(error));

    return {
        body: data
    }
    
}

Sure you do! I expect that anything asynchronous goes under context.async.

That’s what I figured. I’m using this post, along with Help with server-side plugins (with templates) for reference on how to build this out. Thanks!

Will post back with a nice success story :crossed_fingers:

1 Like

Thanks for this @vini_brito. Your tip saved me after three weeks of trying and almost giving up. I think the understanding of arrow function also helped me to understand whats going on.

here is a quick video incase one what a quick understanding of arrow function https://www.youtube.com/watch?v=h33Srr5J9nY

1 Like

Hello everyone that may come across this thread!
I have an adjustment to this that I said:
“I expect that anything asynchronous goes under context.async.”

I do not expect that anymore, actually some libraries containing async code are better left off this function “context.async” and also any code that will be in a workflow that will be executed more than once must also not be contained in “context.async”, for example a simple http request that you will call again.

Why is this? Because “context.async” makes the network request the first time you want it to do, however subsequent calls of that network request will not happen, it will instead just give the response of the first request.
I found that while making the same request and always receiving the same, outdated results, despite laying several instructions in the request for nothing to be cached.

This is disruptive for any function that will run more than once per page load, so if this is your case, do remove your async code from within the “context.async” and use the traditional version instead.
In order to do something with the returned data, you can publish it into a state and then trigger a custom event in Bubble in the end of your async function and in the workflow that runs inside that custom event you’ll have the data available for sure.

For example like this:

  const request = async (url, optionsObj) => {

    let response = await fetch(url, optionsObj);
    let result = await response.text();

    let resultAsObj = JSON.parse(result);

    instance.publishState(`my_state`, anythingYouDoWithResponse)
    
    instance.triggerEvent("query_done") //in this event the data will be available in the custom state



}

  request(properties.endpoint_url, queryOptions);


1 Like

Thank you so much for the tip, it is really something very valuable.
It´s made my day when trying to use the asynchronous API of Google´s Big Query.

1 Like

Hey Vini, thanks for this post. Several times over the past few months I’ve found myself debugging an issue with an asynchronous plugin action and your post here has always been helpful.

1 Like

Hi @vini_brito, many thanks for this post, which helped me understand why I could not get my data from Bubble :sweat_smile:.
So I followed your example, however it still does not work and I do not understand why. Could you please help me ? Many thanks for your help !
Please see my code below :
function(properties, context) {
let varBody={
“mouvement”: “AN”,
“motif”: “AN”,
“compagnie”: “020”,
};
let varBody1 = JSON.stringify(varBody);
let requestData = {
method:‘POST’,
headers :
{
‘Content-Type’: ‘application/json’,
‘Authorization’: properties.Authorization,
},
body : varBody1
};
return context.async( async callback => {
try {
const response = await fetch(‘https://texa.rec-veos.iga.fr/rs/rsExtranet2/contrat’, requestData);
const Reponse = await response.json();
let Id=Reponse.id;
callback( undefined, Id );
}
catch ( err ) {
callback( err );
}
});
}

Hi @elian.faggion,

I’d start by ditching fetch and using context.request instead.
The latter is the only valid method - as far as I can tell - to request data. It’s not very well documented, but I can give you a generic example of how I used it, since I had your very same problem. Here’s my post:

1 Like

This worked for me! Thank you, vini_brito!

1 Like

What is context.async function?
what is the diference?

I’m starting know in this world :sweat_smile:

you are once again a life saver @vini_brito

2 Likes