ToolJet has the capability to connect to PostgreSQL databases for data retrieval and modification.
<divstyle={{paddingTop:'24px'}}>
## Establishing a Connection
To establish a connection with the PostgreSQL data source, you can either click on the **+ Add new Data source** button located on the query panel or navigate to the **[Data Sources](/docs/data-sources/overview)** page from the ToolJet dashboard and choose PostgreSQL as the data source.
ToolJet offers two connection types to connect to your PostgreSQL database:
- **[Manual connection](#manual-connection)**
- **[Connection string](#connection-string)**
### Manual Connection
To connect to PostgreSQL using Manual connection parameters, select **Manual connection** as the connection type and provide the following details:
**Note:** We recommend creating a new PostgreSQL database user to have control over ToolJet's access levels.
:::info
Please make sure the **Host/IP** of the database is accessible from your VPC if you have self-hosted ToolJet. If you are using ToolJet cloud, please **whitelist** our IP.
ToolJet now supports SSH tunnelling for the PostgreSQL data source, allowing secure connections to databases hosted inside private networks. This can be used to :
- Access private databases
- Improve security
- Enable encrytped communication
- Avoid firewall rule changes
#### SSH Configuration
To securely connect to a private PostgreSQL database using SSH tunnelling:
1. Enable the **SSH tunnel** toggle in the PostgreSQL data source configuration.
2. Provide the following details:
- **SSH host** – Server hostname or IP address.
- **SSH port** – Port number (default: `22`).
- **SSH username** – Username for the SSH server.
- **Authentication method** – Choose either:
- **Private key**
- **Password**
Once configured, ToolJet establishes a secure SSH connection. All PostgreSQL queries are routed through this encrypted tunnel.
1. Click on **+ Add** button of the query manager at the bottom panel of the editor and select the database added in the previous step as the data source.
2. Select the operation that you want to perform and click **Save** to save the query.
ToolJet offers support for parameterized SQL queries, which enhance security by preventing SQL injection and allow for dynamic query construction. To implement parameterized queries:
1. Use `:parameter_name` as placeholders in your SQL query where you want to insert parameters.
2. In the **Parameters** section below the query editor, add key-value pairs for each parameter.
3. The keys should match the parameter names used in the query (without the colon).
4. The values can be static values or dynamic values using the `{{ }}` notation.
#### Example:
```yaml
Query: SELECT * FROM users WHERE username = :username
In ToolJet, you can set up server-side row-level security to restrict access to specific rows based on custom groups or default user roles. Refer to the [Setup Row Level Security](/docs/app-builder/dynamic-access-rule/row-level-security) guide for more information.
You can set the timeout duration for SQL queries by adding the `PLUGINS_SQL_DB_STATEMENT_TIMEOUT` variable to the environment configuration file. By default, it is set to 120,000 ms.
### PostgreSQL Dynamic Functions and System Variables
PostgreSQL offers dynamic functions that provide runtime information about the current session, connection, database, and server settings. These can help you write queries that automatically adapt to different environments without hardcoding values.
| Function / Variable | Description | Example Output |
Inserts a new row or updates an existing row if a matching primary key already exists. In the editor, ensure to the input the **Columns** in `string` format.
#### Required Parameters
- **Primary Key column(s)**: Specifies the column(s) used to identify whether a row already exists for updating or if a new row should be inserted.
- **Columns**: Defines the column–value pairs that will be inserted or updated in the row.
<imgstyle={{marginBottom:'15px'}}className="screenshot-full img-full"src="/img/datasource-reference/postgresql/bulk-update-gui.png"alt="Bulk Update using GUI Postgresql"/>
### Bulk Upsert using Primary Key
Inserts new rows or updates existing rows in bulk based on matching primary key values.
#### Required Parameters
- **Primary Key columns**: Specifies the column(s) used to determine whether a row already exists for updating or if a new row should be inserted.
- **Records to Update**: An array of objects containing primary key values and column data that will be inserted as new rows or used to update existing rows.
This basically means If the row exists then update, if not do insert.
<detailsid="tj-dropdown">
<summary>**Example Values**</summary>
```json
[
{
"id": 101,
"activity_date": "2026-03-11",
"category_id": 15,
"notes": "Updated activity entry"
},
{
"id": 104,
"activity_date": "2026-03-14",
"category_id": 18,
"notes": "New activity created via upsert"
}
]
```
</details>
<imgstyle={{marginBottom:'15px'}}className="screenshot-full img-full"src="/img/datasource-reference/postgresql/bulk-upsert-gui.png"alt="Bulk Upsert using GUI Postgresql"/>
- You can apply transformations to the query results. Refer to our transformations documentation for more details: **[Transformation Tutorial](/docs/app-builder/custom-code/transform-data)**