Guide for developers
Postgres query to a chart in an email
An n8n workflow that runs a SQL query on a schedule and emails the result as a chart. The Postgres node reads the rows S3, a Code node turns them into a ChartPNG URL, and the Send Email node puts that URL in an <img> tag. No file, no binary field, no attachment.
The short version
- Import postgres-chart-to-email.json, pick your Postgres and SMTP credentials, and replace the example query.
- The email holds
<img src="URL" alt="...">, a sentence with the main figure and a table of the numbers. The URL is a plain remote image S2. - Some mail clients block remote images until the reader allows them S5 S6. That is why the text and the table are in the email too.
Why a URL in an img tag
Send Email attaches files from binary properties: you enter the name of the field that holds the fileS4. Two forum threads show people struggling with it: one user could not tell what to put in the attachment field nameS1; another saw an attachment listed as "unknown", with Outlook 365 named in a reply S2. When the same user asked for the image in the body of the email, the answer was an img tag with a remote sourceS2.
A ChartPNG /chart URL is that kind of source: it returns a PNG for a config in the URL. The workflow never handles the image, so there is no binary field to name, no file extension to set and no upload step. It only builds a string.
The workflow
Four nodes, and a note on the canvas.
- Every Monday at 8 (Schedule Trigger). For a daily report, set the interval to days instead of weeks.
- Example query (replace) (Postgres, operation Execute Query S3). A
SELECTover a made-uporderstable. Replace it. - Build chart (Code). Reads the rows, builds the Chart.js config and the image URL, the sentence and the table.
- Send email (Send Email, format HTML S4). Puts them in the body.
Import it
- Download postgres-chart-to-email.json, or copy the import link
https://chartpng.dev/templates/n8n/postgres-chart-to-email.json. - In n8n, open the Workflow menu (the three dots) and choose Import from File or Import from URL. The n8n guide has the details.
- Open Example query (replace) and pick your Postgres credential. The file holds the name
Postgres accountas a placeholder, no secrets. - Open Send email, pick your SMTP credential and set From, To and Subject.
- Run Execute workflow once and look at the email. Then publish the workflow (on n8n 1.x, switch it to Active).
The example query, and keeping it read-only
This query is an example over a made-up orders table with created_at and total. It returns the last eight full weeks, one row each. Replace it with your own and keep the three column names, or edit Build chart to match.
SELECT to_char(date_trunc('week', created_at), 'DD Mon') AS week,
count(*) AS orders,
round(sum(total)) AS revenue
FROM orders
WHERE created_at >= date_trunc('week', now()) - interval '8 weeks'
AND created_at < date_trunc('week', now())
GROUP BY date_trunc('week', created_at)
ORDER BY date_trunc('week', created_at);A report only reads. The template's query is one SELECT statement and the tests fail if it ever contains a write or a second statement. That checks the text in the template, not the database, so as a convention, give the credential a database user that has SELECT and nothing else. If you put values into the query, use query parameters: n8n sanitizes data in query parameters, which prevents SQL injection S3.
What goes in the email
The Code node outputs the chart, the image URL, the alt text, a one-line summary and the table rows. The Send Email node uses them in this HTML:
<p>Revenue by week, the last 8 full weeks.</p>
<img src="{{ $json.imageUrl }}" alt="{{ $json.alt }}" width="600" style="max-width:100%;height:auto">
<p>{{ $json.summary }}</p>
<table style="border-collapse:collapse;font-size:14px">
<tr><th style="padding:4px 12px;text-align:left">Week</th><th style="padding:4px 12px;text-align:right">Orders</th><th style="padding:4px 12px;text-align:right">Revenue</th></tr>
{{ $json.tableHtml }}
</table>This is the chart for the sample rows. The footer says "Sample data"; yours says "Source: orders table" until you change it.
The config behind it, which the Code node builds from your rows:
{
"type": "bar",
"data": {
"labels": [
"15 Sep",
"22 Sep",
"29 Sep",
"06 Oct",
"13 Oct",
"20 Oct",
"27 Oct",
"03 Nov"
],
"datasets": [
{
"label": "Revenue",
"data": [
9841,
10420,
9211,
11030,
11890,
11640,
10105,
12480
]
}
]
},
"options": {
"plugins": {
"title": {
"display": true,
"text": "Revenue by week"
},
"chartpng": {
"source": "Sample data"
}
}
}
}The Postgres node can output NUMERIC and BIGINT columns as text S3, so the Code node converts every value with Number() and stops with a message naming the row if a value is empty (NULL) or not a number, instead of charting it as 0. Text from the database goes through an HTML escape before it reaches the email.
When the image does not show
Classic Outlook for Windows blocks automatic picture downloads by default, and the reader can unblock them S5. Nodemailer says many email clients block external images by default S6. So treat the picture as a bonus on top of the text:
- The alt text is the finding in a sentence, so a blocked image still tells the reader the number.
- The summary line and the table repeat the figures as plain HTML.
Sizing, dark mode, Gmail's image proxy and the client differences are in Charts in emails, with sources, so they are not repeated here.
Troubleshooting
- No email and no error. A node that outputs no items normally ends the branch in n8n, so a query with no rows sends nothing. Widen the date range, or check the Postgres node's output.
- "Every row needs numeric orders and revenue columns." Your query uses other column names. Name them
week,ordersandrevenue, or edit the Code node. - The chart URL is very long. A GET URL carries the whole config, so a query with hundreds of rows makes a long URL. Group the data in SQL to a few dozen points. The limits section of the email guide covers long URLs.
- The watermark. This URL is unsigned, so the image keeps the small watermark. A signed URL removes it: see Docs, Signed URLs.
Checklist before you publish
- The query is yours, read-only, and the credential can only
SELECT. - The source line no longer needs to say "Sample data": it says "Source: orders table" in the template, change it to what you chart.
- A test email shows the chart, the sentence and the table. Check it once with images blocked.
- No key or password in the workflow: only the credential names.
More workflows: all guides and the n8n templates.
What we could not confirm
- The Reddit thread https://www.reddit.com/r/PostgreSQL/comments/105vxa4/ could not be fetched, so nothing on this page quotes it or depends on it.
- That a Gmail or Outlook attachment named "unknown" is always caused by a missing file extension on the binary data. S2 shows one case, with Outlook 365 named in a reply, and does not settle it. The workflow here sends no attachment, so it does not meet the problem.
- Which mail clients block remote images and which do not. S5 covers classic Outlook for Windows and S6 says "many" without naming them. The client details are in the guide on charts in emails, with their own sources.
- The Postgres node's default for NUMERIC and BIGINT columns. S3 lists the option and does not state the default in the lines we read, so the Code node converts every value with Number().
- That a Postgres query with no rows stops the workflow. It is how n8n branches usually behave and we did not find it in the pages we read. The Code node also throws on an empty input, for the case where the Postgres node is set to always output an item.
- We did not run this workflow against a Postgres server and an SMTP account. The tests run the Code node on sample rows and render the chart; the query is checked as read-only text, not executed.
Sources
All pages read on 3 Nov 2026. Quotes are exact and short. Web pages change, so check the date.
- S1n8n community: How to Send a QuickChart Graph in Gmail?https://community.n8n.io/t/how-to-send-a-quickchart-graph-in-gmail/90437
Quote: "You can attach this file to the Gmail node directly"
Read 3 Nov 2026. Reply of 20 Mar 2025. The person who asked was sending a chart as a file and wrote: "I’m not sure what I’m supposed to put in the attachment field name". A forum thread, not n8n documentation.
- S2n8n community: When sending an email attachment, the file extension does not show and is displayed as "unknown"https://community.n8n.io/t/when-sending-an-email-attachment-the-file-extension-does-not-show-and-is-displayed-as-unknown/140829
Quote: "you can use regular img with remote source"
Read 3 Nov 2026. Reply of 2 Jul 2025, with an HTML example whose img tag has a remote src. The thread starts on 30 Jun 2025 with the "unknown" attachment; Outlook 365 is named in a reply of 2 Jul 2025, not in the first post, and the attachment problem is not solved in the thread. A forum thread, not n8n documentation.
- S3n8n docs: Postgres nodehttps://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.postgres/
Quote: "Execute Query : Execute an SQL query"
Read 3 Nov 2026. The same page says of Query Parameters: "n8n sanitizes data in query parameters, which prevents SQL injection." It also lists the option "Output Large-Format Numbers As" for "NUMERIC and BIGINT columns", as Numbers or Text.
- S4n8n docs: Send Email nodehttps://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.sendemail/
Quote: "Enter the name of the binary properties that contain data to add as an attachment."
Read 3 Nov 2026. The page also says Email Format HTML will "Send the email in HTML format." and that the node uses an SMTP credential.
- S5Microsoft Support: automatic picture downloads in classic Outlook for Windowshttps://support.microsoft.com/en-us/outlook/block-or-unblock-automatic-picture-downloads-in-classic-outlook-email-messages
Quote: "Microsoft Outlook is configured by default to block automatic picture downloads from the Internet."
Read 3 Nov 2026. It goes on: "You can, however, unblock pictures that you think are safe to download." The page covers classic Outlook for Windows only.
- S6Nodemailer: Embedded imageshttps://nodemailer.com/message/embedded-images
Quote: "Many email clients block external images by default for privacy and security reasons."
Read 3 Nov 2026. Nodemailer is the library behind SMTP sending in many Node apps; this is its own statement about clients.