Files
supabase/apps/docs/content/guides/database/views.mdx
T
Miranda Limonczenko 7ce4ee53ae chore(docs) Retire supa-mdx-lint (#50602)
Closes
[DOCS-1289](https://linear.app/supabase/issue/DOCS-1289/get-the-linter-to-fix-what-it-flags-or-retirereplace-the-linter)

Stacked on #50600, which points contributors at the authoring skills.
Merge that one first.

## Problem

Contributors experienced friction with the linter. They felt nickle and
dimed for tiny nits and felt detracted from the work itself. PRs would
become noisy with tiny one-word suggestions.

Additionally, our homegrown linter is not very intelligent, causing
frequent overrides.

## Solution

This removes the linter entirely in favor of directing contributors to
use SKILLS instead.

The removal entails...

- **CI.** Delete the three `docs_lint` workflows: the PR check, the
external-PR comment companion, and the nightly `--fix` bot. Drop the
stale `zizmor.yml` ignore entry for the deleted workflow.
- **Tooling.** Delete `supa-mdx-lint.config.toml` and the 14 rule files.
Drop the `lint:mdx` script and the `@supabase/supa-mdx-lint` dependency
from docs, learn, and ui-library, and regenerate the lockfile.
- **Content.** Remove the 181 directives. A separate commit carries
Prettier's reformatting of the tables and blank lines those comments had
suppressed, so the deletion commit stays readable. No prose changes.
- **Style guide.** The word list states each rule directly instead of
describing what the linter flagged. Every term survives, including the
phrase groups that mirrored `Rule004ExcludeWords`.
- **Skills.** `write-the-docs`, `edit-the-docs`, and `review-the-docs`
drop `pnpm lint:mdx` from their self-review commands and check the word
list directly. `ask-the-docs`'s CI reference drops both workflows.

## Manual testing

1. Run `git grep -i supa-mdx-lint -- . ':!pnpm-lock.yaml'`. No matches.
2. Run `pnpm install --frozen-lockfile --lockfile-only`. It passes, so
the lockfile matches the three trimmed manifests.
3. Run `git diff master...HEAD --name-only --diff-filter=ACMR | grep -E
'\.(md|mdx)$' | xargs npx prettier --config prettier.config.mjs
--check`. All changed markdown passes.
4. Open the [reformatted filter
table](https://docs-git-docs-retire-mdx-linter-supabase.vercel.app/docs/guides/observability/logs#filter-events)
on the preview and compare it with
[production](https://supabase.com/docs/guides/observability/logs#filter-events).
The table renders the same.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Documentation guidance now uses manual prose and terminology review
with the shared word list.
* Clarified storage configuration and common Realtime channel mistakes.
* Improved table formatting, text wrapping, and selected reference
links.
  * Updated documentation authoring and review guidance.

* **Chores**
* Retired automated MDX linting from workflows and local validation
commands.
* Removed lint-suppression markers throughout documentation without
changing instructions.
  * Added targeted documentation review guidance for pull requests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-22 10:00:41 -07:00

183 lines
6.2 KiB
Plaintext

---
id: 'views'
title: 'Views'
description: 'Creating and using views in Postgres.'
---
Learn what views are and when to use them.
Views sit on top of the tables you create in [Tables and data](/docs/guides/database/tables).
A view is a convenient shortcut to a query. Creating a view doesn't involve new tables or data. When you run a view, Postgres executes the underlying query and returns its results.
Say you have the following tables from a university database:
**`students`**
| id | name | type |
| --- | ---------------- | ------------- |
| 1 | Princess Leia | undergraduate |
| 2 | Yoda | graduate |
| 3 | Anakin Skywalker | graduate |
**`courses`**
| id | title | code |
| --- | ------------------------ | ------- |
| 1 | Introduction to Postgres | PG101 |
| 2 | Authentication Theories | AUTH205 |
| 3 | Fundamentals of Supabase | SUP412 |
**`grades`**
| id | student_id | course_id | result |
| --- | ---------- | --------- | ------ |
| 1 | 1 | 1 | B+ |
| 2 | 1 | 3 | A+ |
| 3 | 2 | 2 | A |
| 4 | 3 | 1 | A- |
| 5 | 3 | 2 | A |
| 6 | 3 | 3 | B- |
Creating a view that consists of all three tables looks like this:
```sql
create view transcripts as
select
students.name,
students.type,
courses.title,
courses.code,
grades.result
from grades
left join students on grades.student_id = students.id
left join courses on grades.course_id = courses.id;
grant all on table transcripts to authenticated;
```
Then you can access the underlying query with:
```sql
select * from transcripts;
```
## View security
By default, Postgres checks permissions on a view's underlying tables against the view's owner rather than the role running the query. If a privileged role creates a view, everyone reading it does so through that role's permissions. Define the view with the `security_invoker` modifier to check the querying role's permissions instead, which is also what makes the underlying tables' row level security policies apply.
```sql
-- switch an existing view to the querying role's permissions
alter view <view name>
set (security_invoker = true);
-- create a view with the security_invoker modifier
create view <view name> with(security_invoker=true) as (
select * from <some table>
);
```
## When to use views
Views provide several benefits.
### Simplicity
As a query becomes more complex, calling it repeatedly gets tedious, especially when you run it regularly. In the example above, instead of repeatedly running:
```sql
select
students.name,
students.type,
courses.title,
courses.code,
grades.result
from
grades
left join students on grades.student_id = students.id
left join courses on grades.course_id = courses.id;
```
You can run this instead:
```sql
select * from transcripts;
```
A view also behaves like a typical table. You can safely use it in table joins or create new views from existing views.
### Consistency
Views reduce the likelihood of mistakes when you execute a query repeatedly. In the example above, you might decide to exclude the course _Introduction to Postgres_. The query becomes:
```sql
select
students.name,
students.type,
courses.title,
courses.code,
grades.result
from
grades
left join students on grades.student_id = students.id
left join courses on grades.course_id = courses.id
where courses.code != 'PG101';
```
Without a view, you need to add the new rule to every dependent query. That increases the likelihood of errors and inconsistencies, and it takes considerable effort. With views, you alter the underlying query in the `transcripts` view, and the change applies to every application using it.
### Logical organization
With views, you can give your query a name. This is useful for teams working with the same database. Instead of guessing what a query does, a well-named view explains it. For example, the name of the `transcripts` view suggests that the underlying query involves the `students`, `courses`, and `grades` tables.
### Security
Views can restrict the amount and type of data presented to a user. Instead of giving a user direct access to a set of tables, you give them a view. You can prevent them from reading sensitive columns by excluding those columns from the underlying query.
## Materialized views
A [materialized view](https://www.postgresql.org/docs/current/rules-materializedviews.html) is a form of view that also stores its results to disk. Subsequent reads of a materialized view return results much faster than a conventional view, because the data is already available. A conventional view executes the underlying query each time you call it.
Using the example above, you can create a materialized view like this:
```sql
create materialized view transcripts as
select
students.name,
students.type,
courses.title,
courses.code,
grades.result
from
grades
left join students on grades.student_id = students.id
left join courses on grades.course_id = courses.id;
```
Reading from the materialized view is the same as a conventional view:
```sql
select * from transcripts;
```
## Refreshing materialized views
There's a trade-off: data in a materialized view isn't always up to date. Refresh it regularly to prevent the data from becoming too stale.
```sql
refresh materialized view transcripts;
```
How often you refresh a materialized view is up to you, and it probably differs for each view depending on its use case.
## Materialized views vs conventional views
Materialized views are useful when execution times for queries or views are too slow. This happens in views or queries that involve multiple tables and billions of rows. Use a materialized view only when you can tolerate outdated data. Internal dashboards and analytics are common use cases.
Creating a materialized view isn't a solution to inefficient queries. Always optimize a slow-running query, even when you implement a materialized view.
## Resources
- [Official Docs: Create view](https://www.postgresql.org/docs/current/sql-createview.html)
- [Postgres Tutorial: Views](https://www.postgresqltutorial.com/postgresql-views/)