Row-Level Security for Dashboards: How It Works

Row-level security (RLS) is a database control that decides which rows of a table a user can read or change, based on who they are, their role or their session context. For a dashboard, that means two customers can open the same chart and see different numbers, because the data layer only hands each of them their own rows. This guide covers what RLS is, how it works in Postgres, SQL Server and BigQuery, where it fails, and how it fits with customer-facing and internal dashboards.
dashrendrapp.dashrendr.comBuild Dashboards for Every CustomerCreate customer-facing dashboards with your data, using a drag-and-drop editor to customise what your SaaS product shows.Try the playgroundTL;DR
- RLS filters rows inside the database using policies, so access rules live next to the data instead of being repeated in every app query.
- It suits multi-tenant SaaS (one shared table, many customers) and any case where sensitivity varies by row.
- The common failures are a table with RLS never enabled, a privileged role that bypasses it, and policies nobody tested for deny cases.
- For embedded dashboards, RLS in the database is one layer. You still need the dashboard tool to pass the right tenant identity on every query.
- dashrendr is an analytics platform for SaaS teams, with plans from $6/month and a 14-day free trial, no credit card required.
Table of Contents
- What row-level security actually is
- How RLS works under the hood
- When to use row-level security
- RLS in PostgreSQL
- RLS in SQL Server and BigQuery
- How RLS works in dashboards and embedded analytics
- Common mistakes and limits
- Our perspective: RLS is a floor, not the whole building
- Where dashrendr fits
- Using RLS for internal dashboards too
- FAQ
- Sources
What row-level security actually is
A normal permission says “this user can read the orders table”. RLS goes one step further: “this user can read the orders table, but only rows where the customer ID is theirs”.
Microsoft describes RLS as using group membership or execution context to control access to rows in a database table, through predicate-based access control. Teradata puts it more plainly: it limits which rows a user can read or change based on identity, role or session context.
Two words matter here. Predicate is just a condition that is true or false for each row (for example, tenant_id = 42). Policy is the rule that attaches a predicate to a table and says when it applies.

The term also shows up in BI tools such as Power BI, where it means filtering report content by user role. The idea is the same, but the enforcement point differs: in a BI tool the filter sits in the report model, while in a database it sits in the engine and applies to every query that touches the table. This article focuses on the database kind, because that is what protects you when dashboards are embedded in your own product.
How RLS works under the hood
When RLS is on, the engine adds your predicate to every query, whether the person wrote a simple select or a join across five tables. The user never sees the extra condition.
Microsoft’s SQL Server docs describe two kinds of predicate:
- Filter predicates silently filter rows out of read operations such as select, update and delete. The query succeeds, it just returns fewer rows.
- Block predicates reject write operations that would violate the rule, for example an insert that tries to create a row belonging to another tenant.
That silent filtering is why RLS feels so clean in a dashboard. A query for total revenue doesn’t need a WHERE customer_id = ... clause. The same query returns the right total for each viewer.
Microsoft also notes that RLS simplifies application security by placing the access-restriction logic in the database tier rather than in another application layer. In practice that means one developer forgetting a filter in one endpoint no longer leaks data.
When to use row-level security
The honest answer to “should RLS be enabled?” is: yes, wherever one table holds rows that belong to different people and more than one of them can reach the database, directly or through a tool.
Typical cases:
- Multi-tenant SaaS. Teradata calls RLS particularly useful for isolating customer data in multi-tenant environments, and for fine-grained access when sensitivity varies by row.
- Regulated data. AWS gives examples such as restricting patient-data access to support HIPAA compliance, or compartmentalising financial client accounts by advisor. Whether RLS alone satisfies a regulation is a question for your compliance adviser, not a dashboard tool.
- Internal teams. A regional sales lead sees their region, support sees tickets for accounts they own, finance sees everything.
When you may not need it: a single-tenant database where every viewer is allowed to see everything, or a setup where each customer already has their own separate database. Separate databases are stronger isolation, but cost more to run and migrate. We cover the trade-offs in our guide to multi-tenant dashboards and data isolation.
RLS in PostgreSQL
Postgres is where most SaaS teams meet RLS first. The PostgreSQL documentation on row security policies says row security applies to normal table access once you enable it with ALTER TABLE ... ENABLE ROW LEVEL SECURITY. Until you do that, policies you create do nothing.
A minimal tenant policy looks like this:
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.tenant_id')::int);
The app (or the dashboard tool’s connection layer) sets app.tenant_id for the session, and every query on orders is filtered. Add a WITH CHECK clause if you also want to stop writes into another tenant’s rows.
Three details trip people up:
- Table owners and superusers bypass policies by default. If your dashboard connects as the owner, RLS isn’t protecting anything.
- No policy means deny. Once RLS is on, a role with no matching policy sees zero rows. That fails safe, but it can look like an outage.
- Policies are code on the hot path. A predicate that calls a slow function runs per row. Index the tenant column and keep the predicate simple.
If you run Supabase, its guidance is blunt and worth following. Its row level security docs say to enable RLS on every table in an exposed schema, keep the service_role key server-side because it bypasses RLS, and revoke existing anon and authenticated grants before granting only what’s needed.
For the connection side of this, see our walkthrough on a secure PostgreSQL dashboard via REST API.
RLS in SQL Server and BigQuery
SQL Server implements RLS with security policies and predicates written as inline table-valued functions. The function returns a row when access is allowed, and the policy binds that function to a table as a filter or block predicate.
BigQuery uses row-level access policies. Google’s documentation says these can coexist with column-level security and project, dataset and table-level access controls, so you can layer them rather than choose one.
BigQuery comes with a caveat that matters for SaaS. Google recommends using row-level security only within an organisation, not across organisations, to help prevent data leakage through side-channel attacks. If your “users” are your external customers, that is a signal to filter by tenant in a controlled layer in front of BigQuery instead of handing customers database identities. Our embedded BigQuery analytics guide goes through that pattern, and the BigQuery cost optimisation piece covers what per-tenant queries do to your bill.
MySQL has no native RLS feature. Teams usually emulate it with views that filter on a session variable or user, as the AWS write-up for Aurora MySQL and RDS for MySQL shows. If MySQL is your store, read our notes on a secure MySQL embedded dashboard before exposing it.

How RLS works in dashboards and embedded analytics
This is the part most explainers skip. A dashboard adds a middleman between the viewer and the database, and the middleman decides whose identity the database sees.
There are three common patterns:
| Pattern | How the tenant is enforced | Strength | Watch out for |
|---|---|---|---|
| Database RLS with per-session context | Tool sets a session value (such as tenant ID) before each query; policies filter rows | Enforced in the engine, so a bad query can’t escape | Connection pooling can leak context if the value isn’t reset |
| Database users per customer | Each customer maps to a database role with its own policies | Strong separation | Hard to manage past a few dozen customers |
| Tenant filter applied by the dashboard layer | Tool injects the tenant condition into every query from a signed identity | Works on databases with no native RLS | The tool becomes the security boundary, so it must be trustworthy and tested |
The rule that holds across all three: the tenant identity must come from your server, never from the browser. If the embed accepts a customer ID that the viewer can edit in a URL or a script, you don’t have security, you have a suggestion.
A second rule: the dashboard connection should use a least-privilege role, never the table owner or a superuser. That one choice decides whether Postgres RLS protects you at all.
The pattern in our own guide on giving each customer their own data without breaking your database walks through the shared-table approach step by step.
Common mistakes and limits
Most RLS incidents are boring. They come from setup, not clever attacks.
- Enabling policies but not RLS. In Postgres, the policy exists, the table is wide open.
- A bypass role in the wrong place. Service keys and owner roles skip RLS. Keep them server-side and out of any dashboard connection.
- Only testing the happy path. Supabase’s guidance is to write tests that assert both allow and deny for select, insert, update and delete, with a policy for each operation. Test as tenant A trying to read tenant B, not only as tenant A reading A.
- Forgetting joins and views. A view can run with its owner’s rights and skip the policy on the underlying table. Check how your engine treats views.
- Crossing organisational boundaries. Google’s advice about side-channel leakage, above, applies to any engine where an external party can run arbitrary queries.
- Treating RLS as the whole answer. It controls rows. It does not hide columns, rate-limit queries or stop someone exporting what they’re allowed to see.
A quick pre-launch checklist
- RLS enabled on every table that holds tenant data.
- The dashboard connects as a role that can’t bypass RLS.
- Tenant identity comes from a signed value created server-side.
- The tenant column is indexed.
- Automated tests prove tenant A cannot read or write tenant B’s rows.
- Exports and scheduled emails go through the same filtered path as the live dashboard.
That last item gets forgotten. A PDF emailed on Monday morning is just as much a data disclosure as a live chart.
Our perspective: RLS is a floor, not the whole building
We build dashrendr for SaaS teams, so we see the same pattern often. A team starts with a chart library, then discovers that the charts were the easy part. Layouts, tenant separation and exports are the work.
Our view is simple. Put isolation as close to the data as your stack allows, and don’t depend on any single layer. If your database supports RLS, use it. If it doesn’t, such as MySQL, or you can’t give the dashboard layer a database role per tenant, make sure the tenant filter is applied by a layer that takes identity from your server and nowhere else.
We’d also push back on one habit: copying every customer’s data into a separate database just to feel safe. Sometimes that’s right, particularly for regulated customers. Often it is a lot of cost for a problem a well-tested policy solves.
Whatever you pick, test it as an attacker would, and test the exports too.
— Paloma
Where dashrendr fits
dashrendr is an analytics platform for SaaS teams. You connect your data sources (Google Analytics, BigQuery, PostgreSQL, MySQL, MongoDB, Google Sheets) or push your own data through the REST API, then build dashboards in a drag-and-drop editor. Dashboards can be embedded in your product so each customer sees their own data, used internally to track your own metrics, or emailed on a schedule as daily, weekly or monthly reports.
Multi-tenant security is part of the product, and it is meant to sit alongside your own database controls, not replace them. Run your tenant policies, connect with a least-privilege role, and let the dashboard layer handle the layout, branding and exports.
Plans start at $6/month (Hobby), with Starter at $15/month, Grow at $39/month and Premium at $99/month. Extra team members are $3/month each. There is a 14-day free trial and no credit card is required. See the pricing page for what each plan includes. If you’re building a customer portal, the white-label portal is the place to start.
Using RLS for internal dashboards too
RLS isn’t only for customer-facing work. Internal dashboards have the same problem with different labels: support should see the accounts they own, a regional manager should see their region, and finance sees everything.
A single internal dashboard with row filtering beats five near-identical copies. Copies drift, and someone always forgets to update the one nobody owns. If you’re setting up operations views, our page on dashboards for operations and support teams shows the kind of layouts teams build, and automated email reports cover the weekly snapshot side.
FAQ
Should row-level security be enabled?
Yes, on any table that mixes data from different customers or users and is reachable through a dashboard, API or direct connection. In Postgres, enabling it is a separate step from writing policies, so check both. Test that a user from one tenant can’t read or write another tenant’s rows.
What does RLS mean in security?
It stands for row-level security: an access control that limits which rows of a table a user can read or change, based on identity, role or session context. The database applies it on every query, so the application doesn’t have to remember to filter.
How does row-level security work in PostgreSQL?
You enable it on a table with ALTER TABLE ... ENABLE ROW LEVEL SECURITY, then create policies with a USING condition for reads and optionally WITH CHECK for writes. Table owners and superusers bypass policies by default, so connect dashboards with a limited role. Tenant context is usually passed as a session setting.
Does row-level security slow queries down?
It can, because the policy predicate runs as part of every query on that table. Keep predicates simple, index the column they filter on, and avoid slow function calls inside them. Measure with and without the policy on your own data before assuming a cost.
Is RLS enough to secure customer-facing dashboards?
No. It controls rows only. You also need server-issued tenant identity, a least-privilege connection role, tests for deny cases, and the same filtering on exports and scheduled emails.
Sources
- Row-Level Security, SQL Server (Microsoft Learn)
- PostgreSQL documentation: Row Security Policies
- Supabase: Row Level Security
- Introduction to BigQuery row-level security (Google Cloud)
- Best practices for row-level security in BigQuery (Google Cloud)
- What Is Row-Level Security? (Teradata)
- Implement row-level security in Amazon Aurora MySQL and RDS for MySQL (AWS)
Recommended
Tags
Build customer-facing dashboards without the engineering overhead
Connect your data sources, design interactive charts with a visual drag-and-drop editor, and embed with native Web Components in minutes.
14-day free trial · No credit card required · Cancel anytime
Embedded Dashboards: A Practical Guide for SaaS Teams
A practical guide to embedded dashboards for SaaS teams. Covers what an embedded dashboard actually is and how it differs from a BI report or hand-coded charts, the three routes into your product (custom build, traditional BI platform, or purpose-built embedding tool), how token-based embedding works with iframes, SDKs, and web components, multi-tenant isolation through row-level security and default-deny access, designing around decision pathways rather than data abundance, the features worth insisting on, and a seven-step first project. Includes Dashrendr pricing, the common mistakes to avoid, and an FAQ.
Worked Example: What It Costs a 200-Customer SaaS to Ship Customer-Facing Dashboards
Most embedded analytics content is either abstract theory or a polished customer story with the hard numbers removed. This is neither. ShiftStack is a hypothetical SaaS we invented to make the math concrete: 200 customers, PostgreSQL, six engineers, and a decision to make. We walk through requirements, multi-tenant architecture, and a full build-versus-buy cost model over three years. Every figure is labeled as an assumption so you can swap in your own.
Data Source Mapping: How to Map Fields to Dashboards
A practical guide to data source mapping for SaaS teams building customer-facing dashboards. Covers what a useful mapping records, designing the target metrics first and mapping back to the source, getting the grain right to avoid duplicate counts, mapping habits by source type (PostgreSQL, MySQL, BigQuery, Google Analytics, MongoDB, Google Sheets, and REST APIs), how mapping shapes dashboard information architecture, and how to set freshness targets so stale data never reaches a screen. Includes a copyable mapping template, a step-by-step walkthrough, and the mistakes that cause double-counted metrics.
