I had never done this before, but it works like a charm!
Thanks, GeorgeâŚ
it was interesting to see how someone else builds.
I was amazed at the amount of reusables you use. It almost seems like every form field is reusable. I think that would really confuse me?
I use reusables for everything, but I usually use them at the group level. Unless itâs something that gets used a whole lot in the appâŚ
like the sidebar on the dashboard page. Would you be reusing that multiple places in the app? Or, for each group on the sidebar, would you be reusing that multiple times?
Is there an advantage to breaking down reusables to the micro level?
I just have a SPA and add reusable groups to the index page.
Maybe youâre setting this up for something that would be used on a huge app with tons of pages?
Also, for option sets, I never create an option set if Iâm just going to use something once or twice in the app.
I thought the UI page was an interesting feature. I donât use that. I just use the style guide. Maybe I should consider a UI page.
Anyway, thanks for the guide. Iâll look it over more when I get more time
Good questions!
The sidebar is a distinct component with its own set of logic. It can and ought to be (in our opinion) separated from the rest of the page logic. As the sidebar gets more complex in an app and has more logic attached to it, it will be useful that it is distinct so bugs are less likely to occur.
I may want to use the same sidebar on a /admin page too, for example.
Reusables have distinct, single responsiblities, so that when you want to change or improve a feature, you know exactly where to do that, and can do so safely without it interfering with another part of the app.
Famous last words, when you want to implement it the third time, then the fourth
Haha that was just for my reference. This template isnât really a design system/component library, I was just using it when making styles!
Thanks, George.
I actually like the style guide page. I thought that was a good thing
I do use a lot of option sets. Everything from images to colors for fonts, borders, backgrounds, navigation, etc. Youâre probably right about wanting to use something more like a third or fourth time in the future.
Thanks again for your example
What kind of logic do you put into a sidebar other than navigation stuff? From my experience, Iâve only ever needed a sidebar for navigation of the page/app, so all the logic is based on changing some value, usually the URL parameter value, that controls what âviewâ is currently on screen. Plus, maybe, there is a logout action.
Other than that, the logic is responsive design logic, which can for simple things can be based only on page width, but for more complex apps/UI you may need to use responsive settings based on other elements on the page. In your setup I see it is based only on page width and is a yes/no value attached to some var group. That will restrict other functionality, since users may need to collapse with a click or a drag. Your approach does make it simpler, but loses flexibility and extendibility.
In your experience, would you use the same data in the side bar for the dashboard as for the admin page?
Why do you not use the same page that is dashboard as the admin page?
In your option set for sidebar, why not make it easier to setup the icons and just use font awesome as an attribute rather than putting so many conditionals onto the single button to change the icon? If you already have so many attributes on that option set, it would seem quicker and easier to maintain everything there, and not need to navigate around the editor so much to find where to add the different icon.
This is confusing. If it has distinct single responsibilities, and is not used in more than one part of the app, it should not be a reusable element. If you have a button as a reusable and that button doesnât have more than one single responsibility it doesnât follow the logic you express here. Generally speaking @senecadatabase you do not want to break reusables down to a micro level. It will make it a real pain to find where things are as you have to go on a happy scavenger hunt and click through each nested reusable to find the micro components. It is easier, if the element, has a single distinct responsibility, to keep it in the single reusable element that is the entire feature set it has the responsibility for.
An example, is that if you break things down into micro level, and distinct pieces, it becomes a nightmare to have to not only find each thing, but to change each thing, because you are either having to open multiple tabs to keep each nested reusable open at the same time, or break your brain trying to remember things from each as you navigate away. Instead it is much simpler to have them all in the one reusable to make referencing other elements quicker and easier, and to find each, since they all live in the single reusable as a feature set, and naming conventions plus Bubble search feature allow us to type in the name of the element and bubble magically shows us it to select.
Iâm personally very strongly against breaking reusables up into microlevel. The reason Iâm strongly against it is that I once over-engineered things and attempted to break them apart, and it was a mistake I was able to self reflect on and learn from. Never again.
Congrats @georgecollier on putting the app in public. It takes some guts to walk the talk. A couple of things I want to discuss-
- Why donât you secure the Stripe public webhooks URL by filtering who can call these API endpoints (filtering based on IP address, as Stripe has a fixed set of IP addresses that call the webhook)? I read the reason you explained, where you mentioned users wonât be able to do anything malicious with the existing setup. My point is, what if a user gets their subscription ID and keeps repeatedly calling the âstripe-subscriptionâ endpoint, or in some other case? Instead of thinking about so many different use cases, why not just put the restriction? Or you can verify the Stripe signature too.
- DB naming convention â sometimes you use camelCase, sometimes you use âBilling Account.â Is there any specific reason behind it?
- Why use the DB trigger? Instead, you can just add the condition for a fallback avatar or updating the âfull nameâ-that will save so many workload units.
- Wonât this condition fail, as you donât set âisSignedUpâ as ânoâ as the default value, and then âisSignedUpâ will be empty?
Nope! Which is why the sidebar generates its list of items from the sidebar item option set, so it can be entirely different. And, itâs pretty self documenting / just âworksâ.
You can see from here that if you wanted to use it on admin page, you need to create an Admin page option, then just add the items you want with the Pages including the admin page and the appropriate role. You know it wonât break anything else.
If the admin page is separate and protected by a server-side redirect, it means I donât need to add workflow conditions on every single workflow on the page, as Bubbleâs security logic will protect them for us.
Doing this would 100% be more maintainable, I just hate font awesome
Most of the time, you donât know if itâs going to be used in more than one place!
I mean, they can do so if they want. They wonât be able to do anything, weâd just incur more WU than maybe is necessary (though if a user wants to âWU-DDoSâ us thereâs plenty of ways to do that). Thereâs absolutely more layers of security you could add to these webhooks:
- Admin token and Current userâs unique ID contains admin
- IP filter
- Webhook signature check
âŚmany of which I spoke about here: Securing Stripe webhooks without authentication
Please see the docs on this but tldr is primitives are camelCase, data types + option sets are Capitalised Like This!
A name could be updated in a few places e.g when an account is creating in the backend by inviting a user, when the user is updating from their profile, when an admin wants to change a user etc. Full name is just a sync of first + last name and it means we donât need to think about it at all for the rest of the appâs development. It wonât save âso manyâ workload, as how often are people really updating their names?
A yes/no cannot be empty in Bubble, if empty itâs evaluated as no.
Oh also, this isSignedUp field is purely for the sake of sign up with Google as sign/up and login are the same action so we know whether to run the signup logic. I guess you worked that out though
George, you continue to amaze me with how much you give to the community â all selflessly and for the greater good! Thanks for being who you are! I donât have an immediate use, but I agree that itâs a shame that this boilerplate canât be easily replicated to be used as a foundation for a new app. (The app is on the free plan and as such, General > Export App is disabled).
Working on putting it in the template marketplace now due to demandâŚ
Check back soon
Actually @akalati I just moved it back to agency plan so it can be exported
Okay, so it is the same data, as it is the same option set, just filtered, as that does in fact strengthen the ability for the sidebar to be a decent component as a reusable. You are filtering that same data set by the role attribute, something I do in my apps as well, so you may just be able to convince me using the side bar as a reusable as something I should also be doing in my apps, as it would be easy enough to fix the drawback of the collapse being just based on a var group set to page width and instead tie it into a custom state so as to make it more versatile.
My questions that would help me understand the benefit of the sidebar as a reusable are revolving around the security.

If the admin page is separate and protected by a server-side redirect, it means I donât need to add workflow conditions on every single workflow on the page, as Bubbleâs security logic will protect them for us.
In that quote you mention that the protection is a server side redirect for admin. In your dashboard page you already do a server side redirect when user is logged out on page is loaded and when a user logs out, so wouldnât that suffice to protect the data as well for Admin usage?
I can see in backend workflow you already have the conditions for Admin and current user is User, which I believe are basically the server side conditional evaluations to protect the deletion of the data. I see a lot of backend workflows with these conditions, which makes me believe that is where the security is, plus the server side redirect on the page is loaded if user is logged outâŚso wouldnât all of that be sufficient and the use of the second page used only by Admin be redundant?
Iâm not familiar with how somebody can change their âroleâ client side as a bad actor, so can not test against my theories of how security would work, so based on my understanding, we have privacy rules that can restrict what data is available to which users, as well as the conditions to evaluate when making changes via backend workflows or the conditions on the actions themselves (which I do not see any used even on the User profile changes), so my assumption there is that it is the privacy rules mostly that are protecting the data from bad actors, then the conditions to evaluate to ensure no trust in the client side data (ie: changing role client side to admin by bad actor) and the server side redirects based on user logged out. Wouldnât just those 3 things (privacy rules, conditions on backend workflows and server side redirects) be sufficient to protect the data and the secondary page be unnecessary and no need to put conditions onto every single workflow on the page?
Iâve always just used the same reusables for CRUD operations for all user types, with conditions for which are or are not available to admin and the privacy rules for protecting which data is available plus constraints on searches while just using a single page for dashboard for all users with the server side redirect on user is logged out. Is that a security concern since there is not a separate page for admin dashboard but it has all the components, server side redirect, privacy rules and backend conditional evaluations?

the drawback of the collapse being just based on a var group set to page width and instead tie it into a custom state so as to make it more versatile
I donât understand what you mean by this, you can change it to anything by using Display Data instead of Set Custom State.

In that quote you mention that the protection is a server side redirect for admin. In your dashboard page you already do a server side redirect when user is logged out on page is loaded and when a user logs out, so wouldnât that suffice to protect the data as well for Admin usage?
Nah, /dashboard is allowed for users and admins. That means that the workflowsâ âhiddenâ conditions are basically âcurrent userâs role contains user or current userâs role contains adminâ. It wouldnât protect all workflows on the page to be admin workflows as normal users also have permission to access this page.

I donât understand what you mean by this, you can change it to anything by using Display Data instead of Set Custom State.
Rando you are correct, you can change it to anything by using Display data instead of Set Custom State.
However.
The group Var is a child element of a reusable element that is the âsidebarâ. It currently already has a dynamic expression in the data source. Iâm sure @georgecollier did that for the simplicity of it, as it is a âset it and forget itâ type of approach based solely on a single breakpoint.
My comments are based on an understanding of little nuances not always on top of mind.
A group var can have itâs data source changed by display data, but when expecting to do that, it is best to just leave the data source empty to begin with.
If you are using a âset it and forget itâ approach as George did, it is not appropriate to incorporate more elements than necessary, since it can just as easily be a conditional with the same âwhen page width is less than the breakpointâ on the reusable element to change the width and doesnât need to incorporate a group as a value source, since the real value source is the current page width as compared against the breakpoint.
When you use display data you are changing that group data value by a workflow action, something that Bubble debugger states clearly that the value it is displaying may not be the current value as âit is changed via a workflow actionâ, which makes debugger more difficult.
When you have a child element on a reusable, it is not possible to reference that child element via a workflow action from another reusable or the page it is on. What that means, is for the reusable sidebar, which has that child group, when you want to change that group value, based on actions from the elements on the dashboard page itself, or perhaps, the width of an element on the dashboard page itself, you will need toâŚ
- Create a custom workflow event on reusable that is simply to display data into the group
- From the dashboard page, you will trigger a custom event from a reusable element
Those two things on there own are not necessarily too much work, but it is not necessary, because if you are adding a custom states in an appropriate location for making it easy to see all custom states (tip: appropriate location for custom states is on the reusable or page level and not a child element) you will have the custom state on the reusable element itself, which means from the dashboard page you can use the set state of an element action to set the custom state of the reusable sidebar.
When you use the custom state you can clearly see in debugger what the value is without the caveat that it might not be accurate, not need any custom workflow events and be able to reference the value of the custom event so as to perform the toggle function you are so fond of (ie: value is no), because the group value is not accessible even when running a custom event from a reusable, so no toggle possible.

It wouldnât protect all workflows on the page to be admin workflows as normal users also have permission to access this page.
Okay, so you are saying than that privacy rules with conditions on backend workflows as you have are not enough and you also need to have conditions on the workflow action itself on the page or reusable. That is because a user can change their âroleâ on client device to be âadminâ and then if the workflow action on the page or reusable doesnât have a condition that causes a server side evaluation, then a malicious actor could perform actions on data protected by privacy rules they are bypassing by changing their âroleâ client side.
Is there any particular reason that you choose to schedule backend workflows which you have conditions on the backend workflow rather than just running the action via workflows on page or reusable and just put the conditions on those instead as that will save the cost of running the âschedule backend workflowâ action. It seems to me like the tradeoff is that youâll pay 0.7 WUs more than necessary since you are still adding the conditions on backend workflow as opposed to the page workflow. Is there any extra security reasons to do it this way?

s there any particular reason that you choose to schedule backend workflows which you have conditions on the backend workflow rather than just running the action via workflows on page or reusable and just put the conditions on those instead as that will save the cost of running the âschedule backend workflowâ action. It seems to me like the tradeoff is that youâll pay 0.7 WUs more than necessary since you are still adding the conditions on backend workflow as opposed to the page workflow. Is there any extra security reasons to do it this way?
Better validate actions on the backend rather remember to validate on every frontend call to this same workflow. Its a boilerplate with 0 functionality except for app shell and subscriptions after all

Is there any particular reason that you choose to schedule backend workflows which you have conditions on the backend workflow rather than just running the action via workflows on page or reusable and just put the conditions on those instead as that will save the cost of running the âschedule backend workflowâ action. It seems to me like the tradeoff is that youâll pay 0.7 WUs more than necessary since you are still adding the conditions on backend workflow as opposed to the page workflow.
Myself and my clients value my time more than the negligible WU costs for things like this
It improves maintainability, which means I need less hours to get things done, which means my clients spend less with us and still get a better job done.

the drawback of the collapse being just based on a var group set to page width and instead tie it into a custom state so as to make it more versatile.
It can be set by triggering a custom event from the sidebar reuseable element to collapse/expand the sidebar. The property is just the source of truth for its current state, used in conditional formatting on its parent page.
Thanks @georgecollier
Questions
-
In global, is there any reason to have logic as isUnauthorized?
- Will it be the same if logic returns isAuthorized and then have the terminate step set to only when isAuthorized is no
-
Is the terminate step safe? e.g. Is it possible to manipulate the value passed to terminate?
-
Beside this method terminate method, what other ways do you recommend protecting front end WFs?
The terminate seems best, as it just stops the WF at the beginning, right after the trigger and still enables propagation of the error message.

Better validate actions on the backend rather remember to validate on every frontend call to this same workflow
Okay thank you. I was and am genuinely curious about whether or not there is a security component to it as I want to learn more about an area I am less familiar with due to my lack of ability to test against users changing values client side to âhackâ the app. So, @akamarski to summarize, there is not security benefit to use one over the other, it is simply just an easier way to do it so as to not forget.

It can be set by triggering a custom event from the sidebar reuseable element to collapse/expand the sidebar.
I understand that. Iâve mentioned to @randomanon that point.

The property is just the source of truth for its current state, used in conditional formatting on its parent page.
Okay, I didnât even recognize you are ALSO incorporating a property into the mix as well. So a group, a custom event and property all just to replace a custom state, okay, to each his own.

my clients value my time more than the negligible WU costs for things like this
It improves maintainability, which means I need less hours to get things done, which means my clients spend less with us and still get a better job done.
And it means that we can charge an effective rate (we donât bill hourly) of well over $200 an hour
Okay, so there is not a security benefit? Remember, I ask because I want to learn, not to question your method. Security is not something Iâve focused on and I default to those who have niched themselves into areas I lack experience in for guidance and learning. I thought this post and app shell were for that.
So to sum up on that conditional evaluation, it is just that it allows you to work faster? I suppose that if clients are charged per hour that tracks as a benefit to the client, but if they pay a subscription fee and only care about things getting done and not how long they take you, than the client may benefit from consideration of their WU costs in how the app is put together.
But, yes, I see the point, it is easier and makes it more maintainable to just call to the backend workflow especially if it is used in more than one place and doesnât already have an action in the series that already confirms the permission of the user via a reusable element with custom events to return a yes/no value of âisUnauthorizedâ. Again, I ask because I am genuinely curious about security things I do not know about. Seeing so much redundancy in the security measures makes me curious if it is all necessary or not since I do not have an ability to change the user role from client device to test these things myself, so I have to ask somebody who is doing it about the rationale for it and the implications of doing it or not.
@georgecollier if a malicious user can change their role via client side, can they also change the return value of âisUnauthorizedâ or is that not possible since the value is not downloaded on page load like âcurrent userâ data is (where âroleâ would be defined and changeable) and being returned in workflow action doesnât give malicious user an opportunity to change it while the actions are running?
Thanks for sharing George, gives me a lot to have to dive deeper into security about.