HR Tech

How Techparser Built a Multi-Tenant HR Platform With Tenant Isolation in the Database

Techparser built a multi-tenant HR platform where each company's data is isolated in Postgres itself, with row-level security on every tenant table.

Client
Techparser product build
Industry
HR Tech
Timeline
Built, available for deployment
Team
1 senior full-stack engineer

Want results like these?




Results

19 migrations
Numbered migrations record the schema history
RLS
Row-level security on every tenant table
Audit
Append-only log, no update or delete

The problem

Techparser built a multi-tenant HR product. Each company is an organisation, and people join it with a role: admin, HR, manager or employee. It covers the people directory, time off, attendance, onboarding, documents, projects, announcements, feedback and reports, with a platform owner who onboards companies from above. Many companies share one Postgres database on Supabase, so isolation had to hold in the database itself, not only in application code. TypeScript runs in strict mode, validation is shared between browser and server, and there is no open admin sign-up. Companies are onboarded through the owner console with one-time setup links.

HR data is among the most sensitive a company holds. One missed company filter in a query would show one company's employees and private documents to another. Two overlapping leave requests approved at once would corrupt a balance. The isolation and the integrity checks had to sit where no application bug could bypass them.

What Techparser built

  • Row-level security on every table that holds company data, driven by a small set of database functions that check membership and role, so employees see their own rows, managers their reports' and admins and HR everything in their company.
  • Server code checks membership, role and permission before acting as a second layer, so a bug in either the database or the application is caught by the other.
  • A separate, server-only provisioning path for the platform owner, used only after the owner check passes and never exposed to the browser.
  • An exclusion constraint that makes two pending or approved leave requests for the same person on overlapping dates impossible to store, with the app turning the rejection into a plain message.
  • Leave changes run as one database function that re-checks permission, locks the balance row, recomputes what is available, moves days between pending and used, and writes an audit entry in a single transaction.
  • Employee documents and leave attachments held in a private bucket and handed out only as short-lived signed links after a membership and ownership check, with a failed save removing the uploaded file.
  • An append-only audit table that allows reads by admins and HR and inserts by members but has no update or delete rule, storing before and after values for sign-ins, leave decisions, settings and document changes.
  • Nineteen numbered migrations recording how the schema evolved, plus a single idempotent schema file that sets up a fresh database in one run, with configuration validated on use so a missing variable fails fast.

Decisions that mattered

Isolation enforced in two layers

HR data leaks when a single query forgets its company filter, so Techparser did not trust application code alone. Every tenant table carries row-level security, written through a small set of database functions that check membership and role for the signed-in user. Server code then re-checks membership, role and permission before it acts. The trade-off is duplicated effort on every path, but it means a bug in either layer is caught by the other, and no single mistake can expose one company's people to another.

Balances the application cannot corrupt

Leave balances are easy to break when two approvals race or a request overlaps an existing one. Rather than guard this in application code, Techparser pushed it into the database. An exclusion constraint makes overlapping pending or approved requests for one person impossible to store at all. Every create, approve, reject or cancel runs as one database function that locks the balance row, recomputes what is available, moves days between pending and used, and writes an audit entry in a single transaction. The application only has to show the result.

Documents and an audit trail that cannot be rewritten

Employee files are private financial and personal documents, so they never sit in a public bucket. Techparser keeps them in a private store and hands out only short-lived signed links after a membership and ownership check, and a failed record save deletes the orphaned upload. The audit log takes the same uncompromising line: the table permits reads by admins and HR and inserts by members, but carries no update or delete rule at all, so entries cannot be changed or removed after the fact, only appended.

Outcome

The platform is built and available for deployment. It delivers a multi-tenant HR product covering the people directory, time off, attendance, onboarding, documents, projects, announcements, feedback and reports, with an owner console that onboards companies from above through one-time setup links.

Because isolation lives in the database, a new company can share the same Postgres instance without any risk of its data reaching another tenant, and new features inherit the same row-level security by default. The append-only audit log and database-enforced leave balances mean the record of who did what, and every leave figure, hold regardless of what the application does. Nineteen numbered migrations and an idempotent schema file let a fresh environment be stood up in one run.

Questions about this project

What technology stack does PeopleCore HR use?
PeopleCore HR is built on Next.js with TypeScript in strict mode and Tailwind CSS for the interface. Supabase provides Postgres, authentication, storage and realtime, and the tenant isolation runs on Postgres row-level security. Zod handles validation, shared between the browser and the server so the same rules apply in both places. Private documents are held in Supabase Storage and served through short-lived signed links.
Why enforce tenant isolation in the database rather than in application code?
Because application code is where the one missed company filter eventually slips through, and in an HR product that mistake shows one company its competitor's employees. Techparser put row-level security on every tenant table through database functions that check membership and role, then kept application guards as a second layer. Duplicating the check costs effort on every path, but a bug in either layer is caught by the other.
Can Techparser build a multi-tenant HR platform like PeopleCore HR for us?
Yes. Techparser built PeopleCore HR end to end: the database schema and row-level security, the leave and attendance logic, document handling, the audit log, and the owner console that onboards companies. The same approach applies to any multi-tenant SaaS where data isolation and integrity have to hold at the database level. Book a call to discuss the HR or multi-tenant product you need built.