Before you start
- Agreement with the data owner. Which tables the report reads, how each number is defined, and who approves the access.
- Connection details and SSL. Host, port, database name and the SSL setup. PostgreSQL’s docs recommend
sslmode=verify-fullfor security-sensitive connections.requireencrypts the connection but doesn’t check the server’s identity. - The right date column. Know which column marks when each result happened, whether it’s a
timestamptzor a plaintimestamp, and the time zone the business reports in.
Build the report query
Create a read-only reporting role
Ask the database owner to create a login that can only read the tables the report needs:
CREATE ROLE reporting LOGIN PASSWORD 'choose-a-long-password'; GRANT USAGE ON SCHEMA public TO reporting; GRANT SELECT ON ALL TABLES IN SCHEMA public TO reporting; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO reporting;GRANT ... ON ALL TABLEScovers tables that exist now.ALTER DEFAULT PRIVILEGEScovers tables created later by the role that runs it, so run it as the role that owns the tables. On version 14 or later,pg_read_all_datagrants read access to everything, which is usually more than a report needs.Limit what the role can run
ALTER ROLE reporting SET statement_timeout = '30s'; ALTER ROLE reporting SET default_transaction_read_only = on;Both take effect at the role’s next login. A user can switch read-only off for a transaction, so the grants above are the real protection; the setting is a safety net.
Total the month in the business’s time zone
Group by month with the time zone the client reports in, and count distinct records:
SELECT date_trunc('month', created_at AT TIME ZONE 'America/Chicago')::date AS month, count(DISTINCT id) AS paid_orders, sum(total) AS revenue FROM orders WHERE status = 'paid' AND created_at >= '2026-10-01 00:00 America/Chicago' AND created_at < '2026-11-01 00:00 America/Chicago' GROUP BY 1;Without
AT TIME ZONE,date_truncon atimestamptzcolumn groups by the session’s time zone, which is often UTC. If the column is a plaintimestamp, find out which zone it was saved in first.Save it as a view
Save the query as a view so every report uses the same definition, and grant the reporting role
SELECTon it. A view runs its query each time it’s read. A materialized view stores the result until you runREFRESH MATERIALIZED VIEW; refreshing withCONCURRENTLYneeds a unique index on the view.Export the result
From
psql, the client-side\copywrites a CSV to your own machine without superuser rights. The whole command has to fit on one line:\copy (SELECT * FROM monthly_results) TO 'october.csv' WITH (FORMAT csv, HEADER)The server-side
COPY ... TOa file needs superuser or thepg_write_server_filesrole, so use\copy.
What to include each month
Numbers from a database carry more weight than estimates, so define them carefully. A monthly report from PostgreSQL should show these six things.
- What each number countsOne line per metric, agreed with the client: “Paid orders, excluding test orders and full refunds”, not just “Orders”.
- The month’s totalsEach metric for the reporting month, totalled in the client’s time zone.
- The comparisonLast month, and the same month last year for a seasonal business.
- A breakdown that explains the changeBy service, location or channel, whichever the client makes decisions on.
- The marketing work beside itCampaigns, pages and changes made that month, so the business result sits next to the activity.
- A source noteWhich database and table the figures come from and when they were read, so later changes can be explained.
Mistakes to avoid
Grouping by month in the wrong time zone
A timestamptz column grouped in UTC moves late-evening orders into the next day, and month-end orders into the next month. Group in the business’s own time zone.
Counting rows after a join
Joining orders to order items or payments repeats each order. Count DISTINCT order ids, or total before you join.
Reporting from the main database at busy times
A heavy query can slow the client’s live system. Use a read replica if there is one, set a statement timeout on the reporting role, and run reports outside peak hours.
Build it in Monthwrap
Monthwrap connects to the client’s database and runs one saved query for each client source. The query returns a date first, then one number per column. Each column becomes a metric in the report, and rows on the same day are added up.
- Connect with a read-only user. An owner or admin enters the host, port, database, user and password in the workspace. If the database has an allowlist, give the administrator the connection address shown in the form.
- Write the query with start and end dates. Use one
SELECT(orWITH ... SELECT) that returns a date and numeric columns, filtered between{start}and{end}. Monthwrap fills in the dates being synced. - Preview, save and report. Preview the totals, save the query on the client’s source, then add the numbers to the monthly report. Queries run read-only, stop after 30 seconds and return at most 5,000 rows.
Questions
Do I need superuser access to report from PostgreSQL?
No. A role with USAGE on the schema and SELECT on the tables is enough to run reports, and \copy in psql exports to CSV without superuser rights.
Why don’t my totals match the client’s dashboard?
Usually the time zone or the definition. Check that months are grouped in the business’s time zone, and that test orders, cancellations and refunds are handled the same way in both.
Is default_transaction_read_only enough to keep the database safe?
No. A user can turn it off for a transaction. Rely on grants that give the role SELECT only, and treat the read-only setting as a second layer.
Does Monthwrap write to the database?
No. Monthwrap checks that each saved query is a single SELECT before sending it, runs it in a read-only transaction, and stops it after 30 seconds. Connect with a read-only role anyway.
Can Monthwrap reach a database on a private network?
No. Monthwrap only connects to databases reachable on the public internet. If the database has an allowlist, add the connection address shown in the Monthwrap form.
Sources
Menus, plans and limits change. These are the pages this guide was checked against on 10 November 2026.