Custom SaaS development, multi-tenant by design
Custom SaaS development by a multi-tenant SaaS developer: tenancy, role-based access, Stripe billing, integrations and background jobs that scale.
- Multi-tenant from day one
- Stripe billing that matches real usage
- Integrations with fallbacks, jobs with retries
- React 19
- React Router 7
- TypeScript
- Tailwind CSS 4
- Node/Express
- PostgreSQL
- Drizzle
- Redis
- BullMQ
- Stripe
- Docker
- GitHub Actions
- Azure
- Vitest
- Playwright
Custom SaaS development is most of what we do. Our founder leads a production multi-tenant platform today, and every Shikinox SaaS engagement follows the same playbook: a data model that survives growth, tenancy decided before the first feature, billing that matches what customers actually use, and a release process you can run without us. This page explains how we handle the hard parts.
Who this is for
- Founders with a validated idea who need a first production version, not a prototype that gets thrown away.
- Product leaders replacing an internal tool or a spreadsheet process with software customers can log into.
- Companies whose single-tenant app now has twenty customers and twenty deployments, and who need one platform instead.
- Teams that already have designers and a roadmap but no senior engineer to turn it into a system.
Problems we solve
Choosing the right tenancy model
Every multi-tenant SaaS platform answers one question first: how is one customer's data kept apart from another's?
A shared schema with a tenant column on every table is the cheapest to run and the simplest to migrate. Every query must filter by tenant, so isolation lives in code and in row-level policies. It is the right choice for most B2B products with many small or mid-sized customers.
A schema per tenant keeps one database but separate namespaces. Isolation is stronger and slow tenants are easier to spot, but migrations run once per tenant and connection pooling needs care.
A database per tenant gives the strongest isolation and the simplest compliance story. It is the right call when customers demand it in contracts or when a single large customer could dominate the load. It costs the most to operate.
We usually combine a shared schema with hostname-based isolation: each customer runs on its own domain, and the request's hostname resolves the tenant before any query runs. Hostname-based multi-tenancy is the model our founder leads in production. We also recommend attaching the tenant id to the database session rather than passing it by hand, and keeping the option open to move a large tenant to its own database later.
Role-based access that stays readable
We model permissions as a small set of named roles with explicit capabilities, enforce them on the server, and reflect them in the UI only as a convenience. Portals for different user types share one identity system with session auth, OAuth providers and two-factor where it matters.
Billing that matches usage
Flat subscriptions are easy. Usage-based billing with Stripe is where most teams get stuck: metering events, reporting them reliably, enforcing spend limits, topping up credits automatically, and keeping your own records in sync with Stripe through webhooks that can arrive late, twice or out of order. We build the webhook handler as an idempotent state machine, store every event, and reconcile on a schedule so finance never has to guess.
Integrations that fail well
A SaaS product that depends on third-party APIs will see them time out, rate-limit and change. We wrap each provider behind one interface, give it a fallback where a second provider exists, and make retries and circuit breakers part of the design rather than a patch after the first outage.
Work that cannot run inside a request
Reports, document assembly, emails and syncs belong in background jobs. We use a queue with retries, dead-letter handling and visibility into what is waiting, so a slow provider never blocks a user.
Changing the database without downtime
A schema with dozens of tables is normal for a serious product. We version every migration, make changes additive first and destructive later, and run them in CI before they touch production.
Knowing what is happening
Structured logs, request tracing, error tracking and a few dashboards that answer "is it working for this tenant" are part of launch, not a later phase. Results from projects our founder has led include 20% lower API latency through query tuning and caching, and 25% faster page loads with SSR and CDN improvements.
How we approach it
- Discover. A free call on your goals, users and constraints. You get a written scope within 48 hours.
- Design. We agree the data model, the tenancy model, the role matrix and the integration boundaries before heavy building starts.
- Build. Two-week sprints with a weekly demo of working software. Tests land with features, not after them.
- Scale. Launch behind feature flags, watch the dashboards, tune the slow queries, and add engineers as the product earns it. Handover documents are included.
What we build with
We choose tools for how they behave in year three, not how they demo in week one.
React 19 with React Router 7 and TypeScript gives us server rendering, typed routes and a component model your future hires already know. Tailwind CSS 4 keeps styling fast and consistent across portals.
Node with Express on the server stays simple to reason about and easy to staff. PostgreSQL is the database for almost every SaaS product we see: relational data, strong constraints, row-level security. Drizzle gives us typed queries and versioned migrations without hiding the SQL.
Redis and BullMQ run background jobs with retries and scheduling. Stripe handles subscriptions, metered usage and invoices. Docker and GitHub Actions make every deploy the same deploy, and Azure hosts it, or AWS if you are already there. Vitest covers the logic and Playwright covers the user journeys that must not break.
If your team already runs on a different stack, we will tell you honestly whether to keep it.
A project our founder has led
Our founder is lead developer on a tenant screening and property management SaaS for the Canadian rental market. Every property company runs as an isolated tenant on its own domain. Four role-based portals share one identity system with Google and GitHub OAuth and TOTP two-factor. Identity, bank, credit, rental-history and court-record checks from 6+ providers merge into one automated PDF report with provider fallback. Stripe usage-based billing enforces spend limits and tops up credits automatically through webhooks. BullMQ and Redis workers run the slow parts, a 59-table Drizzle and PostgreSQL schema carries versioned migrations, and Docker-based CI/CD ships to Azure through GitHub Actions with Vitest and Playwright suites.
What you get
- An NDA signed before you share any details.
- All code, designs and IP in your repositories from the first commit. You own it completely.
- A weekly demo of working software, not a status report.
- A written scope within 48 hours of the first call.
- A reply to every message within 24 hours.
- A team that starts in 1 to 2 weeks.
Related services: AI integration for features inside the platform, dedicated teams once the product needs more hands, and technical leadership if you want the architecture reviewed first. Every model is a custom quote; see ways to work with us.
Ready to talk? Start a project brief. Four quick questions, then a free 30-minute call.
Questions clients ask us first.
Something else on your mind? Start a project brief and we'll reply within 24 hours.
Shared schema or a database per tenant?
Shared schema with hostname-based isolation for most B2B products; a database per tenant when a contract demands it or one customer could dominate the load. We decide this in the Design step and keep the path open to move a large tenant later.
Can you take over a SaaS codebase we already have?
Yes. We start with a short review of the data model, tenancy, billing and deploy pipeline, write down what we found, and agree what to fix first before adding features.
Do you build the MVP or the whole product?
Either. A fixed-scope project suits a first release; a dedicated team suits a product that keeps growing. Both include a senior lead, weekly demos and code in your repositories.
How do you keep Stripe and our database in sync?
Webhooks are handled idempotently, every event is stored, and a reconciliation job compares our records with Stripe so late or duplicate events never corrupt a balance.
Which cloud do you deploy to?
Azure is where our founder runs production today; AWS works just as well. We use Docker and GitHub Actions so the pipeline is the same wherever it lands.
Ready when you are.
Four quick questions, then a free 30-minute call.