Templates
Reusable view-driven data presets you can apply into any compatible table
Overview
Templates let you define a read-only view that produces rows and then copy those rows into a target table on demand. They're perfect for repetitive seeding tasks:
- Generating this month's invoices from active subscriptions
- Issuing payslips for all currently employed staff
- Creating monthly payment placeholders for active hostel allocations
- Bootstrapping default settings for newly onboarded accounts
A template is just a SQL view tagged with {"type": "template"} in its comment. Supasheet picks it up automatically and exposes it under /$schema/template and as an action on compatible tables.
How It Works
- Create a view that returns rows shaped like the destination table.
- Tag the view with the
templatetype — and optionally atarget_tablehint — in its comment. - Grant
selecton the view to the appropriate roles. - Open the resource at
/$schema/template/$templatein the UI, or callsupasheet.apply_template(...)from SQL.
When applied, Supasheet inspects both the view and the target table, intersects their columns by name, and runs:
INSERT INTO <schema>.<target_table> (<common columns>)
SELECT <common columns> FROM <schema>.<template_view>;Only columns that exist in both the template view and the target table are copied. Defaults, generated columns, and audit fields on the target take over for the rest.
Defining a Template
-- Step 1: Add the permission
ALTER TYPE supasheet.app_permission
ADD VALUE IF NOT EXISTS 'hostel.monthly_payment_template:select';
-- Step 2: Build the view that produces the desired rows
CREATE OR REPLACE VIEW hostel.monthly_payment_template AS
SELECT
a.id AS allocation_id,
a.monthly_rent AS amount,
date_trunc('month', current_date)::date AS month,
'pending'::payment_status AS status
FROM hostel.allocations a
WHERE a.status = 'active';
-- Step 3: Tag the view as a template (optionally name its preferred target table)
COMMENT ON VIEW hostel.monthly_payment_template IS '{
"type": "template",
"name": "Monthly Payment Template",
"description": "Pending payment entries for all active allocations. Apply to hostel.payments to seed a new billing month.",
"target_table": "payments"
}';
-- Step 4: Grant access
REVOKE ALL ON hostel.monthly_payment_template FROM authenticated, service_role;
GRANT SELECT ON hostel.monthly_payment_template TO authenticated;
INSERT INTO supasheet.role_permissions (role, permission) VALUES
('x-admin', 'hostel.monthly_payment_template:select');Comment Metadata
type TemplateMeta = {
type: "template"
name?: string // Display name in the templates list
description?: string // Helper text
caption?: string // Short summary shown on cards
target_table?: string // Default destination — pre-selects the table in the apply dialog
}target_table is only a hint — the user can apply the template to any compatible table in the same schema as long as columns line up.
Applying a Template
From the UI
- Open Templates in the schema sidebar (or browse to
/$schema/template). - Pick a template — Supasheet shows the preview, description, and column list.
- Click Apply, choose the target table (defaults to
target_tableif set), and confirm. - Supasheet calls
supasheet.apply_templateand reports how many rows were inserted.
From SQL
SELECT supasheet.apply_template(
p_schema => 'hostel',
p_template_name => 'monthly_payment_template',
p_target_table => 'payments'
);
-- Returns the number of rows insertedThe function uses security invoker, so it inherits the calling user's RLS context — only rows visible to the user are copied, and the target table's INSERT policies are still enforced.
Permissions
| Action | Required permission |
|---|---|
| List templates in a schema | select on each template view |
| Apply a template into a table | insert permission + RLS on the target table, plus select on the template view |
Templates are visible only to users who can read the underlying view.
Discovery Function
Behind the scenes, the UI calls:
SELECT * FROM supasheet.get_templates('hostel');This returns every view with {"type": "template"} in its comment that the current user can read. The function is defined in supabase/migrations/20260506000001_templates.sql.
Practical Examples
Issuing a payroll batch
CREATE OR REPLACE VIEW hr.payslip_template AS
SELECT
e.id AS employee_id,
current_payroll() AS payroll_id,
e.salary AS gross_salary,
0::numeric AS deductions,
e.salary AS net_salary,
'pending'::payslip_status AS status
FROM hr.employees e
WHERE e.status = 'active';
COMMENT ON VIEW hr.payslip_template IS '{
"type": "template",
"name": "Monthly Payslip Template",
"target_table": "payslips"
}';Bootstrapping default categories on new tenant onboarding
CREATE OR REPLACE VIEW blog.default_categories_template AS
SELECT name, slug, description FROM (VALUES
('News', 'news', 'Latest news'),
('Tutorials', 'tutorials', 'How-to articles'),
('Announcements','announcements','Product announcements')
) AS t(name, slug, description);
COMMENT ON VIEW blog.default_categories_template IS '{
"type": "template",
"name": "Default Categories",
"description": "Seed a fresh blog with standard categories.",
"target_table": "categories"
}';Best Practices
- Keep the view deterministic. Templates are most useful when each application creates a predictable batch.
- Name columns to match the target table. Anything that doesn't match is silently ignored.
- Use sensible defaults (
status,created_at) so users don't end up with rows that need follow-up edits. - Lock down with RLS. Combine the view's
selectpolicy with the target table'sinsertpolicy so only authorised users can apply. - Pair with audit logging. Templates can insert many rows at once — audit them just like any other write.
Next Steps
- Audit Logs — Track rows created via templates
- Authorization — Permissions on the template view and its target
- Reports — Reports follow the same
type-tagged view pattern