DEV Community

Cover image for I’m sick of $200 SaaS boilerplates that leak tenant data. Here is how to build real isolation.
Jackson Kasi
Jackson Kasi Subscriber

Posted on

I’m sick of $200 SaaS boilerplates that leak tenant data. Here is how to build real isolation.

🤢 I’m sick of "SaaS boilerplates" that charge you $200 just to wrap a framework and leave you to figure out multi-tenant database isolation yourself when your first enterprise customer signs up.

The complexity trap
Most templates start clean, but turn into this within a month.

Your if (user.orgId === req.body.orgId) checks are going to leak data eventually. It's just a matter of time. You don't need "magic" hidden in node_modules. You need an explicit, boring, production-grade foundation.

I got tired of rebuilding the same complex isolation architecture, so I built FlowStack.

Today, I’m open-sourcing the organization-v2 branch. No paywalls. No games.


🏗️ Why "Boring" Architecture Wins

When you are building a B2B SaaS, your architecture needs to respect strict boundaries. FlowStack is built as a Turborepo monorepo to enforce this separation physically, not just logically.

  • 📦 apps/server (The API)
  • 🖥️ apps/web (The Client)
  • 🔐 packages/auth (Identity & Isolation)
  • 🗄️ packages/db (Type-safe Postgres Schemas)

If a package doesn't need to know about the database, it doesn't get access to it. Zero Leakage.


⚡ The Engine: Hono + Bun

Cold starts are the enemy of good UX. By running the API on Hono and Bun, the server responds in milliseconds.

High Performance Engine
Speed isn't a feature; it's a foundation.


🔐 True Multi-Org Isolation (Powered by Better Auth)

Most developers spend weeks building invitation flows, password resets, and role-based access controls (RBAC).

FlowStack integrates Better Auth with custom plugins to handle this natively. Let's look at how we automatically resolve active organizations on login without exposing it to the client:

// Inside packages/auth/src/auth.ts
databaseHooks: {
  session: {
    create: {
      before: async (session) => {
        try {
          // Auto-assign the user's first organization
          const userMemberships = await db
            .select()
            .from(memberTable)
            .where(eq(memberTable.userId, session.userId))
            .limit(1);

          if (userMemberships.length > 0) {
            return {
              data: {
                ...session,
                activeOrganizationId: userMemberships[0].organizationId,
              },
            };
          }
          return { data: session };
        } catch (error) {
          logger.error(`Failed to set active organization: ${error.message}`);
          return { data: session };
        }
      },
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

This ensures the session always knows its boundaries before the first request is even processed.


🎨 The Frontend: React 19 + Vite 7

Modern Workspace

We didn't compromise on Developer Experience (DX).

React 19 & Vite 7 for immediate HMR.
TanStack Start for flawless routing.
Tailwind CSS v4 for the styling engine.


🚀 Try It Yourself

If you want magic "one-click" illusions that break in production, go buy a template.

If you want a production-ready monorepo that won't make you rewrite your entire auth logic in 6 months, fork FlowStack.

git clone -b organization-v2 https://github.com/jacksonkasi1/FlowStack.git
bun install
bun dev
Enter fullscreen mode Exit fullscreen mode

Star the repo if this saves you 100+ hours of architectural headaches!
👉 GitHub: FlowStack/organization-v2

Let me know in the comments how you handle tenant isolation in your current stack!

Top comments (0)