@opena.international
Hello Mr.(s) Potential!
My course goes as deep as the rabbit hole goes. I made it to be the definitive material, so if there is something you need that is not covered in there, I will create that material for you.
Most of the content is born out of doubts and challenges we face.
In it, I expect you to know essential HTML, CSS and Javascript. For example what is a variable, a string, an array etc.
And if you don’t, no problem! I do have a learning script (há!) for you to follow and learn to code from zero to the required essential level in three weeks of part time effort.
My course is not focused on teaching basic web development, so this learning script points to a specific curated set of external materials that are excellent at that and can be done entirely free, only one of these resources have a cheap paid option to access extras.
Also, if you buy my quick consulting support option, I do offer mentorship and guidance in your coding journey, not only in your plugin making one, as both are deeply connected.
Specifically on server side:
It is indeed a powerful tool for us.
I have several articles on it, covering it all from the ground up, including pre-solving specific challenges that we face, for example when we want to generate a file in a server side action, like a docx or PDF, and then send that file to the Bubble app, how to do it? I got it covered step by step.
Or if you want to pull a file from the Bubble app or somewhere else in the web so you can process that file, I got a step by step on that too.
Ok, I told, now I will show: Here is the first article on server side that you meet in my material!
There are others covering specific situations like using asynchronous code and dealing with files, however this one is the one that initiates you. It comes after dozens of other more basic articles, so it assumes you already have the knowledge of these more basic ones.
It is text and images and I and my team are currently recording the video version of my entire material for the students that prefer video, so both versions will be available.
Formatting here is a bit off, but here it is!
Hello! In this article you will learn what is the server side action available in the plugin editor, how to use it, its limitations, when it’s a good choice to use it and when it’s a bad choice to use it.
On property fields: it works just like a client side action.
Let’s start by describing it:
-
It always is a workflow action.
-
It can run in workflows triggered on a page but will still be server side, in other words, even if you trigger it from the browser, it will run on the server side.
-
It can run in backend workflows.
-
That server side is a Node environment. This means that almost everything you see related to Node you’ll be able to run there.
-
It is a serverless function. In short, it means you don’t have to manage a server, you write a function, send the serverless provider a request to run it and they run it.
-
Speaking about the provider: AWS Lambda function. And It’s good to know this because in very rare occasions you may run into limitations related to this provider specifically.
-
It uses Node version 12. So if the package you want to use requires a different version it won’t work out of the box and you will need to input additional work. Usually packages are just fine on this version.
-
It can only run for up to 30 seconds. When it hits that, it halts and sends a “time out” error.
-
It can only use 128mb of memory. So if you want to process a file or set of files bigger than this inside the action, it won’t work. So no heavy file processing here.
-
It consumes app capacity based on how long it runs. App capacity is how much computing power the Bubble app has available.
This is a good solution to challenges that involve one or more of these:
Dealing with secret keys.
For example let’s say that you want to implement an email plugin for your app to send emails, that action must be server side so your secret key from the email API provider remains secret with you. Client side actions send all the data to the browser, which means that anyone accessing the page can see any data in it, for example secret keys.
If it’s a key from the user and each user has their own key, save that to your database and protect it with the proper privacy rule. Then pass that key in a field of the server side workflow action. It will be safe this way.
Doing actions without user interaction in the backend workflow.
For example my plugin PDF Conjurer, its main version works client side. Which means its main version works in the browser and only there. So if you want to schedule a backend workflow that generates a PDF report every week then you will need another plugin. The user would need to open the page every week and click a button “Create report” for your flow to work. So no automatic stuff here, like automatically generating the report every week and emailing that to your user.
Using node packages from NPM.
Sometimes packages and libraries only have a Node version, then you’re limited to running it on a server side action. It’s unlikely you will be able to run that on the browser without a good chunk of effort adapting it to do so, but if you really want to tinker with it, you can try exploring the library or package and transforming it or using some other solution or tutorial do run Node stuff on the browser. But only do this if you can’t build that on your own from scratch and there are no alternatives, because it’s preferable to simply find an alternative that runs fine on the browser out of the box.
On limitations:
Not always a library that runs on the browser will run on Node. There are some workarounds to this, to provide a browser-like API inside Node, but these patches aren’t 100% failproof. As I said, it’s best to find an alternative that works out of the box in the way you want, only tinker with patches if you find no alternative and can’t build it yourself.
Tip: Sometimes you can find polymorphic variations of the library or package you want, so try searching for “polymorphic name of the package/library” and maybe you’ll find something good. Worth trying, takes a few minutes only and can save your life
Polymorphic means it runs everywhere.
Bad aspects and when not to use it:
When you need speed in the page. And by speed I mean instantaneous. Why? They have an inherent delay of 1 to 2 seconds. Usually 2 seconds. If you only have this code “return 5”, which means zero data processing, zero downloading, zero computing etc etc it will take 2 seconds for the request to reach Bubble’s server, Bubble’s server directs it to AWS Lambda, AWS Lambda takes 1 second to wake up the function (really!), then runs it and sends the answer back to Bubble’s server who then sends the result of it to the browser. This takes 2 seconds.
So, if there are no secrets and you can run the function in the browser through a client side action, please do so, it will be faster for the user.
If it needs to be done on the server, then provide your user with a good wait time experience. Here are some interesting readings on this:
https://usabilla.com/blog/how-to-integrate-waiting-time-user-experience/ and 7 Rules to Manage User Wait Times | by Chris Kiess | Prototypr
Alright, now with these concepts in your mind, let’s talk on how you use it!
Specifically: An easy development flow, how to require packages, how to return data and debug the action without going crazy and finally how to convert your local script into a Bubble-friendly version.
On the general development flow:
I will be outlining it in the next sections, but here I will begin with some concepts.
You will work with two environments. Your local node and the plugin editor server side actions.
You will first make your script work in a standalone manner in your local node.
Why? Because there we can know what’s going on, in other words, there we can debug our code by seeing all errors in the terminal and we can also do our additional logging with console.log() just like we do in the browser.
The server side workflow action is almost a blackbox and it’s not Bubble’s fault, debugging serverless actions is tougher anyway.
The server side workflow action does logs to the browser console many errors that it throws if you are using the app in the editor mode, which is the “debug_mode=true” we see in the URL and this helps, but most errors and issues will simply be a silent failure. The action will run, nothing will happen and you will keep wondering what could possibly be going on in there.
So we first make it work in our computers, then we convert it to a Bubble friendly format to insert it in a server side action.
On how to require packages you found by googling or searching directly on NPM:
It’s pretty simple.
You visit https://www.npmjs.com/package/is-not-oddand take a look. There is a lot of info, but since I already know I want it, we will only look for installation instructions.
We will now install it in our local node setup. I assume you already installed Node in your computer. If not, check the setup article.
Open up your terminal in the folder that your javascript file is. If you’re not familiar with the terminal, here are some good readings on it.
All you need to know is how to navigate folders on it, nothing else:
A Linux Command Line Primer | DigitalOcean
Navigate your system - Learn the Command Line in Terminal - OpenClassrooms
Also, being on VSCode (or its less invasive brother, VSCodium) also helps as its integrated terminal already starts in your current working folder.
On Ubuntu/Debian or any other Linux system with Gnome installed, you can just open the folder through the normal visual file explorer, right click on empty space to bring the context menu and select the option “Open in terminal”. Super handy.
Ok, now that you are in the folder, you run this command in your terminal:
npm install is-not-odd
I know that the instructions there also add a --save argument, but it’s not necessary, so I removed it.
Anything other than “npm install name” is unnecessary unless you are doing some special setup, which is never needed for making plugins.
After that, you can go to your script and place this in its first line
const isNotOdd = require('is-not-odd');
Now I’ll just paste the sample code:
const isNotOdd = require('is-not-odd');
console.log(isNotOdd('0')); //=> true
console.log(isNotOdd('2')); //=> true
console.log(isNotOdd(1)); //=> false
console.log(isNotOdd(3)); //=> false
And the result of calling this script would be having this logged to your terminal console:
true
true
false
false
You can play with it a little. Try changing the zero to a one inside the script. See, you can now debug what’s going on inside the script and this is how you start.
You can write any other logic here, once you fully tested it with console.log()'s and knows it works fine, then you can take it to the plugin editor.
Open your plugin, go to Actions tab, then press “Add a new action” button. Give it any semantic name that the user will know what is it about, for example “Check if number is odd”. Make the name active and descriptive. Your users will thank you throughout the eternity for this. In the dropdown besides the name select “Server side” and the “Category” dropdown you can leave the default that is “Generic plugin”, this only changes where the action is shown in the workflow action builder, so please only pick another category if it is a real fit else you will confuse your users. Most actions you will create will be “Generic plugin” anyway.
P.S: My screen is black because personally I love using a dark theme, so I have a browser extension called “Dark reader”. It works super fine!
Fields are just like the client side action, no news here, gladly. Else we would just have increased complexity.
In here you have everything you have in a client side action. And of course no “element” properties nor “instance” object, since we are working on the server and not in the page.
In other words, here you will only have the “properties” object to get the input you want from the bubbler through the fields in the workflow action.
The new stuff here is the “Returned values” section, so let’s focus on it.
You must return an object with a specific structure.
There are many ways for you to write that but they are all the same logic, just wearing different clothes.
Let me show you one example:
Here you can specify any field type you want, including app types to ask the bubbler to specify a data type (thing type). See the article on fields to know more on this.
I have selected “text”.
Here’s what your code must resolve to in the end:
return {
email_body_ready: "this is the text that will be returned as a result of this workflow action to the bubbler"
}
You will actually write any logic to form the text, in my case I was creating an email body HTML with inlined CSS and saving the final result in this variable
Here’s how the code would look like:
function(properties, context) {
const purify = require("purify-css");
const juice = require('juice');
let fullCss = `a,body,h1,h2,h3,h4,h5,h6,p,t and a lot more CSS text I'm leaving out`;
let htmlEmail = properties.unprepared_email_body;
let onlyHtml = "<html><head></head><body>" + htmlEmail + "</body></html>";
let purifiedCss = purify(onlyHtml, fullCss);
let emailWithPurifiedInlinedCss = juice(onlyHtml, { extraCss: purifiedCss, insertPreservedExtraCss: false });
return {
email_body_ready: emailWithPurifiedInlinedCss
}
}
Stick to this return structure and you will be happy. It is visually easy to know what is going on and when it is being returned.
Then to the Bubbler, this is how this exact server side will show up and can be used.
And finally, once you paste the code, if you are using any NPM packages through the require(“package-name”) you need to check “This action uses node modules” and then click that red text that will appear at the bottom of the page (I wrote that “latesta” typo on purpose so I could refresh the rebuild option, you will not need to do this):
If everything goes right, it will look like this:
Super awesome, we are mostly done now, if you are not using asynchronous functions. You will rarely have problems here and need to debug your code if it worked in your local Node setup and you are not using asynchronous code.
Now, if you are still having issues with either normal or asynchronous code, is sure that it works fine in your local setup, let’s go after some power debugging techniques and even a permanent logging solution.
If you haven’t already, go read the article on using asynchronous functions, especially the part on changing an async function to work on server side.
The technique I will outline below will be useful for you if your code works in your local node setup but fails in the server side action environment. This will rarely happen, but what if it happens…?
I will not allow you to fall prey to this issue and give up building that new feature.
In some cases you will be able to use console.log(). This is how it goes:
function(properties, context) {
// any code
console.log("hello server log")
// any other code
}
You get this here:
Note the last log with plenty of information, the logged info is right at the “INFO” section.
Good old logging.
But what if we find logic errors? Like undefined functions and the likes. These errors should not happen here since we sorted them out in our local node environment, but here’s how thrown errors look like:
The wrong code:
function(properties, context) {
// any code
console.log("hello server log")
let asynC = await();
// any other code
}
Here’s how it look in the browser for you in case you are using the “debug_mode=true”, the alert:
And the console error:
And in the app’s log:
There it is! Now you can debug anything that happens!