Ask HN: How come there is no example code for B2B-SaaS apps?
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 ] threadhttps://spark.laravel.com/
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"
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
https://docs.microsoft.com/en-us/azure/sql-database/saas-dbp...
https://www.aspnetzero.com/Features
It's a bit pricey but if you don't need the UI or billing then you can use their open source boilerplate code which gives you multi-tenancy:
https://github.com/aspnetboilerplate/aspnetboilerplate
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.
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.
So no point of overthinking it.
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.
From: https://www.indiehackers.com/post/17-saas-starter-kits-boile...
Probably more than that one.
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.
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.
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.
Everyone below a certain scale. Or when it just makes more sense, like Proboards' free tier.
And then providing enterprise service for large companies that want their own installation.
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.
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.
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.
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.
So for simple queries, fine, but anything else this won't work.
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.
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.
https://influitive.io/our-multi-tenancy-journey-with-postgre...
Interested?
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.
They also have a separate repo to help you run it: https://github.com/getsentry/onpremise
https://youtu.be/6wuaVWKVclo
https://jumpstartrails.com/
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
https://github.com/Internal-Tools-Hub/CorpHome/blob/master/R...
There are plenty of paid products in every language which are basically B2B SaaS templates.
ABP, bullettrain, Laravel etc.
https://news.ycombinator.com/item?id=23054417
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
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.
If you are doing Rails by any chance this is just what you need. https://leanpub.com/multi-tenancy-rails-2