How to return objects from plugins (SSA, CSA, Element Update & Element Workflow Actions)

This stumps so many of us when we first start with the plugin editor that I wanted to make a couple quick videos to demonstrate returning an object from your plugin.

Video 1:

Returning an object from a server-side action


– those notes I was writing are below.

https://api.thecatapi.com/v1/images/search


Getting an object back from the server side actions seems impossible at first but it actually really easy. Let’s start by creating a new plugin! 



We will start by heading to the api editor, adding a ‘dummy’ api call, and then defining the object that we want to have returned to us! 

To do this, we’ll need to use a ‘dummy’ url so that bubble knows we mean business (or that they know it’s going to work. They put a safeguard on the api calls so you MUST call a valid API service at least once and it must return something. Otherwise, without calling the dummy API, you cant return the object appropriately. 



SO, let’s go! 

P.s. we’ll be using the URL at the top of this doc for the dummy API call


So notice what i did there. 

I initialize the API, save the call, and then click manually enter the response. 

Here is where we will define the object we want to return. 


And sometimes the api editor is a pain, so let’s validate that JSON

Fun fact, the editor doesnt like malformed jsons or single quotes. So fix that! 

{
"string":"asdfasdf",
    "boolean": false,
    "number": 4,
    "list":["asdf","asdf"]
}

After you click save, the editor will infer the data type each value should be

Make sure those dropdowns have the correct data type. Bubble doesnt like the single quotes. Use the double quotes 

“”






Now, let’s define our server action to return some data based on the model we provided. 


In the SSA you will need a field to define the type of data we we want to return. It must be “App Type”. What this will allow  us to do is use the API response we just defined as the model of data to return within the app. 

Actually, we need to do one more thing. Set the returned value.  Set the return value to the field type we set in the prvious step. 

If you plan to return a list of that model you created, set the return value to list. 


IMPORTANTTTT. You must append each key name with 

_p_

This lets the bubble engine display and interpret things correctly. 


You must also remember to return the list of objects


Let’s check it out.

But of course, if you’re not returning correctly, it wont work. So,

You’ll need to return your data as an object that has your list of objects. 

Like this

We’ll now test out the returned data. 

Add a button to start the action!

One thing we need is the RG to display the list. We’ll set the element to have the data type that we just defined.
Sometimes the editor won’t update the name appropriately. Perhaps this is a bubble bug to be reported?



Video 2:

Returning an object from a client-side action

{{not yet recorded}}
— this one is more difficult but definitely doable!

Video 3:

Returning an object from a plugin element’s update field.

Now that you’ve learned to return them from server side actions, let’s float over to the element itself and see how to return objects there. 

Note, we aren’t updating the API call in this video, just doing it again for show. 



So in short to return the object, 

You’ll need to 

Instantiate an api call to any service and define the return data yourself. 



Now that our API call is ready, we will head over to the element editor and work with it there. It’s a very similar process to the SSA. 

Create the element, define an input as App Type

And define an exposed state whose data type is the input you just defined in the last step. 

What this means is when Data Out publishes any data, it will be in the format defined in the ‘datatype’ input field







With actions that happen client side, we can only return objects using instance.publishState. We dont return an object the same way as in SSA, we only set it up the same way

And that should be it. 

Always test though. 


Even if the element wont have a visual element, it must be dragged on the screen..

Oh yeah. Now that we’re element side, the data comes out in an exposed state which can be bound to another element as the data source. An element like a RG.


We didn't set the exposed state to be a list

Be mindful that any change in the plugin editor will require a refresh in the app editor to be observed. 

Video 4:

Returning an object from a plugin element’s workflow actions

— as a note. you don’t have access to instance from workflow actions. BUT YOU kinda DO if you publish instance to window.

you could do something like this

So what you do is save instance to the window (a truly global variable that can be shared between elements and plugins. 

Then you call instance.publishstate from your window variable. 


Other than that, theres no difference. 
31 Likes

Thank you for these videos! I have found the closed (insider) nature of plugins to be frustrating and limiting. Your videos were a big help in solving various API interactions that require JSON parsing, and how to return that data to our App.

1 Like

I found it difficult too. Now to make the videos a little more friendly with voiceover, let’s cuts, and more clear instructions!

Glad you found it useful!!

We shouldn’t be limited by lack of knowledge. Doing this allows you to expand your reaches quite a bit in my opinion.

1 Like

I am astounded how you ever managed to figure this out but I am in awe! :raised_hands: :raised_hands: :raised_hands: :raised_hands:

Super useful.

1 Like

thanks for the support @lindsay_knowcode the real gold nugget is saving the instance from an element to the window variable. then you can literally publish data between elements.

that and using the API as a data type. it’s :moneybag: :moneybag: :moneybag:

3 Likes

Awesome! You are a bubble-hacker for real!

1 Like

This one automatically handles that stuff (helpful for large/nested objects):

1 Like

This is only relevant for SSA right? Can we use this client side? Looks like it requires a node module to be imported

Yup.

/**
 * @param {Object} obj
 * @param {string} [param_prefix] - _p_ (default) or _api_c2_.
 * @return {Object}
 */
let convertObject = (obj, param_prefix) => {

    if (typeof obj !== 'object' || (obj && Array.isArray(obj))) return {};

    if (typeof param_prefix !== 'string' || !param_prefix || (typeof param_prefix === 'string' && !['_p_', '_api_c2_'].includes(param_prefix))) param_prefix = '_p_';


    /**
     * @param {Object} obj
     * @param {string} [key_parent]
     * @param {boolean} [is_array]
     * @return {Object}
     */
    const convert = (obj, key_parent, is_array) => {

        let result = {};

        Object.keys(obj).forEach(key => {

            let cell = obj[key], key_new = `${param_prefix}${key}`;

            if (key_parent && !is_array) key_new = `${key_parent}.${key}`;


            if ((!cell && cell !== 0 && cell !== false) || typeof cell === 'undefined') {


                result[key_new] = null

            } else if (typeof cell !== 'object' && !Array.isArray(cell)) {


                result[key_new] = cell

            } else if (typeof cell === 'object' && !Array.isArray(cell)) {


                result = Object.assign(result, convert(cell, key_new))

            } else if (Array.isArray(cell)) {

                if (typeof cell[0] === 'object') {

                    result[key_new] = [];

                    cell.forEach(value => {

                        result[key_new].push(convert(value, key_new, true))
                    })

                } else {

                    // It's not an object array, so treat
                    // it as an array of Bubble primitives.
                    //
                    result[key_new] = cell;
                }
            }
        });


        return result
    };

    return convert(obj)
};
6 Likes

Awesome! I hadn’t thought too far ahead! I may find use for this!!

Thanks for sharing!

1 Like

Hi @jared.gibb , Thanks for recording videos and putting out here. It help me a lot.

I am trying to return custom objects from plugin action and somewhat able to do so. But go stuck with the last segment. Since bubble plugin need “p” prefix with each key but that is not possible (in my case- as I am getting data from third party API), how to map “p” prefix key with the normal key (without p prefix).

Hope you are able to understand what I am trying to say.

For Instance-

Screenshot 2021-12-10 at 3.36.38 PM

This type of data required by bubble but I am getting without “p” prefix data. Any help you can do it?

Thanks a lot in advance.

Check this out

const arrayofObjects = [
      {
            name: 'John',
            age: 30

      },
      {
            name: 'Peter',
            age: 25
      },
      {
            name: 'Amy',
            age: 40
      },
];

// 1. write a function that appends _p_ to the beginning of each key in the object
let newArray = arrayofObjects.map(function(item) {
      let newObj = {};
      for (let key in item) {
            newObj['_p_' + key] = item[key];
      }
      return newObj;
}
);
console.log(newArray);
1 Like

Thanks it works like a charm.

2 Likes

@lottemint.md

Sorry this might be a very newbie question but how would I import this module into my plugin? I know how to do it using cdnjs as source links in html but I’m not sure how to do it from github as I can never find these links.

@lottemint.md has hosted this on NPM and the module can be drawn directly down into your server side function. It’s called json-to-bubble-object and you add it simply by declaring some variable using require('json-to-bubble-object') - see the example below.

It will then automatically be added to the Node package.json though you’ll need to click on the red warning message to perform an initial sync of the module.

4 Likes

Yup.
It also can be run client-side if you extract the code from this module:

Thanks @lottemint.md @exception-rambler I’ve got the module working. I’m now having trouble returning the data. what function would I use to return the data. The response I’m getting from the module is

{"_p_mins":5,"_p_price":"36389.29688301"}

This method I have here works

The api_response field is just a test field that returns the api response. I have tried the following

but that isn’t working. There must be a simple function that I’m missing.

For some reason when I try these methods, I get null responses, which I know is me doing something wrong because when I display it under the “api_response” it returns correctly

This has been solved! Thanks got it working

Wanna share your solution or what was keeping you from passing information along?

ofcourse, I need to get into the habit of doing this. My solution was the following code

var body = JSON.parse(response.body);

const {convert} = require('json-to-bubble-object');

var bodystr = function(body){
  Object.keys(body).forEach(function(key){ 
      if (typeof body[key] == "number"){
      body[key] = JSON.stringify(body[key]) }
  		else 
  		{body[key] = body[key] }});
  return body;
}
        
let returnlist = []
returnlist.push(convert(bodystr(body)))
                
    return {
        "result": returnlist
    
    }
}

I think I got to the answer by trying out a bunch of different combinations but key things I had to do was make sure all elements of the object literal were strings (as numbers threw an error), and the convert function for the json-to-bubble-object was setup properly.

EDIT: see the following post for better script, I was getting errors with returning numbers in this script because when I initialised the APIs in the API connector, I didn’t specify number to some variables which were numbers.