Ask HN: How come there is no example code for B2B-SaaS apps?

159 points by node-bayarea ↗ HN
I'm truly baffled! There are probably 1000s of consumer (B2C) SaaaS example and OSS apps in virtually all languages. But there is virtually ZERO examples or OSS projects that talk about B2B multi-tenant apps! The worst part is that even companies like Google Cloud don't provide an example.

What I'm looking for is: A SaaS app that allows people to create tenants (companies), and allows admin of the tenant to login and manage group of users, keep track of billing for each tenant, have super-admin who can access any tenant. But the rest of the app code is shared across the tenants. I'm looking for code that's either in Java or JavaScript(NodeJS). Are you in the same boat as me? If not, could you please point me to something? I really appreciate that!

93 comments

[ 2.9 ms ] story [ 152 ms ] thread
(comment deleted)
I'm not sure you'll find something with such a specific requirement.

The first thing that came to mind is server management tools like free alternatives to cpanel/webhostmanager

https://froxlor.org/

You'll have better luck searching online if you look for "free / open source" + specific problem/solution rather than "b2b saas app"

in this example i searched for "free alternative to cpanel"

Why? It’s difficult and knowledge of how to do single and multi-tenant applications for B2B keep a ton of people employed. However it’s not that far off from consumer projects. Most established players moved fast to build custom code to match their use cases. It’s not OSS material.

For B2B, “super-admin” should only be done with logged permission. Usually an email request.

Here’s two projects to help point you in the right direction.

https://github.com/terraform-google-modules/terraform-google...

WorkOS is attempting to simplify this. https://m.youtube.com/watch?v=IR2QZQrzoiA&t=1433s

Thanks for the shout-out! :)
What is the problem just build it your self. A simple template that has a login with a todo list can be extended to have a companies table. When you have added this, you can add a n to n relation between companies and users and poof you have a B2B saas app template.
I think the request is more for guidance about how to do it right.
I’ve been trying to find some guidance too, specifically for authentication with B2B and how to be adaptable to work with different IAM/SSO platforms
Having built a number of these, it's not that simple. For example do you put a tenant column on every table, have a separate table per tenant, or have a separate schema or database? There are tradeoffs for each approach and lots of gotchas you wouldn't expect. Like if you use a separate table or database, migrations take forever, you can run into file descriptor limits in your database, etc.
That's true, but just looking at the code of some example application you've found online won't shed any light on what those trade-offs are.
Probably not, but there are articles on the subject that are helpful. I've found them before.
Separate table per tenant? Not unless you have/expect 2 to 5 tenants otherwise you run into the issues you describe above.

Having a database per tenant has always interested me. It make sense if the databases will be different (contain other tables)

Adding a tenant id to the tables that require them makes sense.

The best approach is to create a pivot table between users and tenants that holds user_id and tenant_id. Use the primary id of that table to represent the user at that tenant.

The limit is a lot higher than 2 to 5, but I think sooner or later it will get you. Until then you will suffer a slow death by a thousand migrations.

A separate database is best used if you need an absolute firewall between the tenants for security reasons. Almost no bug can ever let one tenant see the data from another. If they share tables, it's easier to make that mistake.

The downside is you can't have quieres across all your tenants, so you'll want a BI database that brings all the data together anyway.

This leads to answer, which is: just start building, whatever option you will choose you will run into gotchas.

So no point of overthinking it.

That there are tradeoffs with each option does not mean all options are equal at all.

Doing a little research first, and thinking about which tradeoffs matter most in your particular case could save you a lot of effort rewriting things down the road.

I've been working on something like this for Ruby on Rails in case anyone is interested. It has multitenancy, billing separate for each tenant, automatic query filtering for the current tenant, etc. https://jumpstartrails.com
For the unfamiliar, OP makes awesome Rails tutorial videos and is really knowledgeable on the subject.
Intereting. I've been thinking about doing something like this with Python (Flask/Django), albeit a bit more general.
Doesn't open stack do this?
I have been working on this:

https://github.com/userdashboard/dashboard

Dashboard powers the basic 'web app with registrations' and then using modules it can be supplemented with organizations, Stripe Subscriptions and Stripe Connect to standardize / reuse the "SaaS boilerplate". It runs parallel to your application server so you can use whatever stack you like. Users browse your Dashboard server URL and it proxies your application server for content.

The software is complete but it needs niceties like useful charts and information added to the UI and I'll be prioritizing that in the coming weeks. It's got a really nice documentation site being generated with Github Actions but it's not publishing correctly unfortunately, probably tomorrow that will be resolved.

I think because it's much easier to do deploy templates than multitennancy.
(comment deleted)
In my experience, a lot of projects start off with shared tenancy (using a tenant id discriminator in the database) and eventually migrate to completely separate schema for each tenant (i.e. a separate “installation” of a single-tenancy app for each tenant) to avoid security and architectural pitfalls.

Once you’ve done that, you don’t have to worry about forgetting a `WHERE tenantId = foo` somewhere and leaking data. The actual code is shared (i.e. not installed separately or in separate processes) and you would simply use your dependency injection or equivalent to resolve the DB connection contingent on $user’s validated claims.

“Networking” or sharing entities across tenants becomes harder this way.. but purer/cleaner. You essentially create a federation layer and communicate across instances via a hard code wall (via network requests to yourself and an API) rather than “in memory” which is less efficient and requires a lot of boiler plate, but it’s going to be what you’re going to end up doing anyway.

> you don’t have to worry about forgetting a `WHERE tenantId = foo

I found a few tools early on that work for this in Rails. You can guarantee all of your sql includes the tenant.

- Apartment

- Tenancy

There's also several for the PHP ecosystem.

Interestingly, I've only seen the opposite, going back to the 90s. Eventually managing N schemas ends up being difficult, and you're also fighting against the database.
Yeah 100%. Multi-tenant as implemented this way with separate infrastructure works ok for very small values on N. Once you’re into N > 100s or 1000s then you’re absolutely not reducing complication.

    WHERE tenantId = foo
You only need to do that if you keep multiple tennants in one table. But who does this? Usually in a multi tennant application you have only one line which connects to the tennants DB and thats it. The rest of the code does not have to think about tennants.
> But who does this

Everyone below a certain scale. Or when it just makes more sense, like Proboards' free tier.

The approach I've used and seen the most often is enforcing the tenancy at the ORM level so someone can't forget a where.

And then providing enterprise service for large companies that want their own installation.

On the enterprise software I worked on (IWMS system) we did both. Customers got their own schema, but big customers would give each branch their own virtual tenant within that schema. We started out with only the multiple schemas approach and ended up retrofitting a bunch of multitenancy features.
using an ORM it's very simple. once you setup the relationships between models, you always use the highest level model as the entry point of your query and the ORM will scope the query for you. ie in Rails, if your highest level model is Account you just always use @account.customer.find(18). the Rails ORM will automatically scope the query to the current account.

using multiple schemas becomes a nightmare when you want to perform a migration to alter the database schema. you have to somehow perform a migration across every single schema without breaking anything. god forbid you need to rollback. have everything in one database makes it really easy. as please, don't talk how using a single database restricts scaling later on. with how far databases have come and how cheap cloud computing is, scaling isn't an issue anymore.

The highest level entry point makes so much sense. I’ve been building multitenant saas for last 3 years enforcing “where tenant = x” and you’ve just blown my mind.
On more than one occasion I've started with a simple table for all tenants (where tenant = x) because we didn't know at the time if we will have many.

Then progressively when we understood how the service will be used, refactored to keep separate tenant's data separate.

Ended up with each tenant in a given schema, we were using Java and relied on Hibernate (ORM) to help connecting to the correct DB (it does support multi-tenancy) as well as on a DB migration tool (Liquibase) so any changes we made to the schema get replicated over all tenants.

that's why we're here... learning something new everyday is a great things. now imagine how cool it will be implementing this is your projects from here out :)
I come more from a JVM world. But aren't there any DB migration/refactoring tools in Rails to allow for changes to propagate across tenants ?

E.g for the JVM, there's Liquibase and Flyway, and I agree with you, setting that up took a good part of a couple of weeks of dedicated effort.

> the Rails ORM will automatically scope the query to the current account.

While that is true for simple queries it is not a valid statement for secure multi tenancy.

For example in this scenario: Account (Id). Order (Id, AccountId, CustomerId). Customer (Id, AccountId). You need to ensure Order can only use CustomerId that belongs to Account.

Rails does not include support for restricting deep relationships (grand children) to an account. Basically an attacker can misuse foreign keys from other accounts in new records (add a CustomerId from another Account on Order) or read data in relationship queries when initiating objects (show customer info from another account in an order).

There are ways of mitigating this but it requires doing more complex manual validation methods.

> using multiple schemas becomes a nightmare

There are gems that will solve some of this complexity but it is a risk. The most popular one is somewhat unmaintained.

You can't really write reports or BI dashboards in ORMs, unless you want terrible performance.

So for simple queries, fine, but anything else this won't work.

no kidding and you're being unreasonable... yes, what ii stated will get you 99% of the way there. what you're belly aching about is the 1% where you have to veer from this and do it yourself. big deal.

look... you wouldn't forego taking a plane across the country cause you have to take a cab the last 2 miles to the hotel. same thing here. just because something falls out at the last 1% doesn't mean it a bad way to do things.

In my limited experience, companies start with single-tenancy apps. Get a client interested in their own private version, so they spin-up another instance.

Then they realise they've got a growing number of databases and webservers to maintain and account for, so they combine them all into a multi-tenant system.

Thanks for this clear explanation. I’m a non developer currently experimenting with no code apps, I’m using userid filtering to segregate data between users and it feels “wrong” but it’s reassuring to see that it’s a pretty normal starting point before having a more mature architecture
I'm contemplating writing an article about "things that are bad but you should absolutely do anyway when you start" to help with this analysis paralysis.

Interested?

Sorry I just saw this (usually just lurk HN so not up to speed with my comment responses), yep interested to help if you'd like
Its a while ago now and I have no idea what post it was for, but there was a discussion here on HN about how this is a terrible idea as it turns into a complete mess and a nightmare to manage.

I've only done this at one company, and they self-hosted, and it was indeed a complete nightmare to manage, but part of that was the self-hosting part.

That's kind of their daily bread & butter.
I know it's a little early and it will be in Ruby on Rails. However, I'm working on open sourcing a clone of Stripe's Home [1] as part of a series of open source B2B applications & they should check all those boxes (besides the Java/Node)

I'm also happy to walk you (or others in the thread if time permits) though the source code of a closed source app I have over a video call if you need to grok the mechanics/db structure. (email in profile.)

It's got:

- multi-tenancy

- roles with-in each tenant

- sub-tenants (think engineering team vs accounting team vs hr team)

- accounts can be members of multiple tenants

- billing for each tenant (even sub tenants or inherit from root tenant)

- Super Admin & "Staff" roles

- Auditing

If anyone's interested in the open source app, follow here - https://twitter.com/tools_hub

[1] - https://stripe.com/blog/stripe-home

Interested to see how you guys approached that for a Java B2B service. Thanks for the offer.
I'm starting a SaaS that adds multitenancy to an app as a service.
And then add in a user authentication system like Auth0 or Cognito with a way to pass authentication tokens among microservices within the app. All stuff that should be boilerplate samples but in practice burns an enormous amount of time getting in place.
http://abp.io/ is probably the best open source one I've seen. They also offer a paid product which is fairly comprehensive. I don't quite like their frontend stuff but the backend side of things is definitely what you're looking for.

There are plenty of paid products in every language which are basically B2B SaaS templates.

ABP, bullettrain, Laravel etc.

looks great, I wish there was something like this in the Node.js world
You should look into Feathers JS [0]. You've the option to write in Javascript or Typescript and its built on top of nodejs/express.

Feathers JS has got builtin authentication and it is easy to add tenants. I've created a quick example and published it here [1], there maybe some rough edges, please mail me if you have any questions.

As others have mentioned with multi tenancy you must have good test cases that verify you've appropriate filters in place to avoid leaking data.

[0] https://feathersjs.com/ [1] https://github.com/bjacobt/feathers-multi-tenant-example

There's little sample code because some parts of the problem are disproportionately harder than others.

For example, in saas, if you have a serious product then you'll soon find that some of your tenants want to use SSO. So you need not only the stuff you mentioned already but also admin screens for setting up e.g saml for individual tenants. Add that to your points and you don't need sample code, you need a whole product (we use keycloak, there are plenty of other options).

As to billing, that's a whole gigantic separate area and again something where you don't want sample code, you want a complete product (I have heard jbilling talked of favorably).

In short, the problem space is too big to be addressed with some sample code.