Connect Retool to PostgreSQL using the native PostgreSQL Resource — enter your host, port, database name, and credentials, and Retool proxies all queries server-side. PostgreSQL is the most common Retool Resource and the foundation for Retool Database itself. Write raw SQL with full support for prepared statements, CTEs, window functions, and JSONB queries. Most teams have a working CRUD admin panel connected to their PostgreSQL database in under 15 minutes.
| Fact | Value |
|---|---|
| Tool | PostgreSQL |
| Category | Database |
| Method | Retool Native Resource |
| Difficulty | Beginner |
| Time required | 15 minutes |
| Last updated | April 2026 |
Why connect Retool to PostgreSQL?
PostgreSQL is the most widely used database backend for web applications, SaaS products, and internal tools — and Retool is built around the assumption that PostgreSQL is your primary data source. Retool Database, the managed database product built into Retool, is itself a PostgreSQL instance. The native PostgreSQL connector is the most mature and feature-complete database resource in Retool, with support for every PostgreSQL feature from basic CRUD to advanced window functions, JSONB document queries, and Common Table Expressions.
The core use case is building admin panels that give operations teams direct, structured access to your application database without requiring them to write SQL or use command-line tools. A standard Retool-PostgreSQL admin panel includes: a searchable Table showing application records, a Form for creating new records, inline editing with a Save button that runs parameterized UPDATE statements, a Delete button with a confirmation modal, and aggregate charts showing key business metrics derived from the same database.
Beyond basic CRUD, Retool unlocks PostgreSQL's advanced analytical capabilities for non-technical users. Window functions for running totals, CTEs for readable multi-step queries, JSONB operators for querying semi-structured data, and full-text search with pg_trgm are all directly usable in Retool's query editor. Teams that previously needed to write Python scripts or build custom dashboards can access the same capabilities through a Retool interface.
Integration method
Retool includes a native PostgreSQL connector with direct support for all PostgreSQL versions. Configure the resource with your host, port, database name, username, and password — Retool handles connection pooling, SSL, and query parameterization automatically. All queries run as server-side prepared statements, separating SQL code from user-supplied values to prevent SQL injection. The connector supports SSH tunneling for databases behind firewalls and AWS IAM authentication for RDS instances.
Prerequisites
- A running PostgreSQL database (any version from 9.6+) accessible over the network
- Database hostname or IP address, port (default 5432), database name, and credentials
- A database user with the minimum required privileges (SELECT, INSERT, UPDATE, DELETE on relevant tables)
- Network access: either the database accepts connections from the internet, or you have SSH tunnel credentials, or you are using a self-hosted Retool instance in the same VPC
- For Retool Cloud: Retool's IP CIDR ranges whitelisted in your database firewall or security group
Step-by-step guide
Configure the PostgreSQL Resource in Retool
Navigate to the Resources tab in Retool and click Add Resource. Select PostgreSQL from the database resources section. Give the resource a clear name — PostgreSQL Production or your application name followed by DB (e.g., Acme App DB) works well. In the configuration fields, enter your database host (IP address or hostname), port (5432 by default for PostgreSQL), database name (the specific database within your PostgreSQL server — not the server itself), and the username and password for the database user. For local development databases, the host is typically localhost but this only works for self-hosted Retool instances; for Retool Cloud, you need a publicly accessible host. If your database uses SSL — which is strongly recommended for any internet-accessible database — select the appropriate SSL mode from the dropdown. Retool supports require, verify-ca, and verify-full SSL modes. If you have a custom SSL certificate (for self-signed certs), paste the certificate contents into the SSL CA field. For databases behind a firewall or in a private VPC, Retool supports SSH tunneling: enable the SSH tunnel toggle and enter your bastion host details (hostname, port, username, and private key). Click Save. Retool tests the connection with a simple SELECT 1 query and shows a green Connected status if successful.
Pro tip: For AWS RDS, the host is your RDS endpoint (e.g., mydb.abc123.us-east-1.rds.amazonaws.com). Enable SSL mode 'require' for RDS. Retool also supports AWS IAM authentication for RDS — enable this in the resource's authentication settings instead of using a static password.
Expected result: The PostgreSQL Resource shows Connected in Retool's Resources list. You can now create SQL queries that run against your database.
Write your first parameterized SELECT query
Create a new query in the Code panel, select your PostgreSQL resource, and confirm the query mode is set to SQL (not GUI mode — that is for write operations). The query editor is a standard SQL editor with syntax highlighting and autocomplete. Write a basic SELECT query to fetch records from a table. Use Retool's {{ }} syntax to parameterize values that should come from UI components — Retool automatically converts these to prepared statement parameters, preventing SQL injection. For example, to filter by a search input, write WHERE email ILIKE {{ '%' + searchInput.value + '%' }}. The {{ }} expression is evaluated as JavaScript, so string concatenation, conditional expressions, and component references all work. If a component has no value (empty text input), the parameterized query receives an empty string — you may need to conditionally include filter clauses. A common pattern is to use a WHERE clause that matches all records when the input is empty: WHERE ({{ searchInput.value === '' }} OR email ILIKE {{ '%' + searchInput.value + '%' }}). For pagination, reference a Pagination component or Number input: LIMIT {{ pagination.pageSize }} OFFSET {{ (pagination.page - 1) * pagination.pageSize }}. Enable Run query on page load in the query settings so it fetches data automatically.
1-- Example parameterized SELECT with search and pagination2-- All {{ }} values become prepared statement parameters (SQL injection safe)3SELECT4 u.id,5 u.email,6 u.full_name,7 u.plan_tier,8 u.created_at,9 u.last_login_at,10 COUNT(o.id) AS order_count,11 COALESCE(SUM(o.amount_cents), 0) / 100.0 AS total_spent12FROM users u13LEFT JOIN orders o ON u.id = o.user_id14WHERE15 u.deleted_at IS NULL16 AND (17 {{ searchInput.value === '' }}18 OR u.email ILIKE {{ '%' + searchInput.value + '%' }}19 OR u.full_name ILIKE {{ '%' + searchInput.value + '%' }}20 )21 AND (22 {{ planFilter.value === 'all' || !planFilter.value }}23 OR u.plan_tier = {{ planFilter.value }}24 )25GROUP BY u.id, u.email, u.full_name, u.plan_tier, u.created_at, u.last_login_at26ORDER BY u.created_at DESC27LIMIT {{ pagination.pageSize || 50 }}28OFFSET {{ ((pagination.page || 1) - 1) * (pagination.pageSize || 50) }};Pro tip: Retool's prepared statements mean {{ }} values are always treated as data parameters, not SQL fragments. You cannot use {{ }} to inject table names, column names, or SQL keywords — only values. For dynamic column names, use a JavaScript query to build the SQL string programmatically.
Expected result: The query returns rows from your PostgreSQL table, filtered by the search input and paginated. The response inspector shows each row as an object with named columns.
Build an editable Table with inline updates
Drag a Table component from the Component panel onto the canvas. In the Table's data source field, set it to {{ getUsers.data }} (using the name of your SELECT query). The Table renders with one column per field returned by the query. Configure columns using the Columns section in the Table's right panel: set column labels, enable Editable on fields users should be able to modify (like plan_tier or full_name), set number formatting on monetary columns, and configure date formatting on timestamp columns. The id and created_at columns are typically display-only (not editable). Enable Row selection so users can click a row to see its data in other components. Add a Save Changes button below the table. Create a new query using GUI mode for the update operation: select your PostgreSQL resource, switch to GUI mode (if not already), set Table to users, Action to Update rows, and configure the filter to WHERE id = {{ table1.changesetArray[0].id }}. However, for bulk updates across multiple changed rows, use a JavaScript query that iterates over table1.changesetArray — the array of all rows the user has edited — and triggers the update query for each row. Wire the Save Changes button's onClick event to this JavaScript query. Add an On success event handler to refresh the main getUsers query and display a success notification.
1-- GUI mode generates this SQL (shown for reference) — use GUI mode UI, not raw SQL for updates2-- For raw SQL update with the selected row:3UPDATE users4SET5 full_name = {{ table1.selectedRow.full_name }},6 plan_tier = {{ table1.selectedRow.plan_tier }}7WHERE id = {{ table1.selectedRow.id }};Pro tip: Use GUI mode for UPDATE and DELETE operations — Retool generates the SQL with proper parameterization and adds a validation step that shows you the generated query before executing, reducing the risk of accidental mass updates.
Expected result: Users can edit fields directly in the Table. Clicking Save Changes updates the modified rows in PostgreSQL and refreshes the table with the latest data from the database.
Add INSERT and DELETE operations
For inserting new records, drag a Form component onto the canvas or create a Modal that contains input components. Add TextInput components for each field (email, full_name, plan_tier). Create a new query using GUI mode: select your PostgreSQL resource, switch to GUI mode, set Table to users, Action to Insert row, and map each column to the corresponding input component value: email → {{ emailInput.value }}, full_name → {{ nameInput.value }}, plan_tier → {{ planSelect.value }}. Wire this query to a Save button inside the Form. On success, trigger the main getUsers query to refresh the list and close the modal. For DELETE operations, create a query using GUI mode with Action set to Delete rows and configure the filter as WHERE id = {{ table1.selectedRow.id }}. Add a Confirm action to the Delete button's event handler — configure a confirmation message like Delete {{ table1.selectedRow.email }}? This action cannot be undone before the delete query runs. Never write a DELETE without a WHERE clause in Retool; use Retool's admin settings to restrict destructive SQL if needed. Enable Row selection required in the Delete button's disabled property by checking {{ !table1.selectedRow.id }} — this greys out the button when no row is selected.
1-- INSERT example with form input references2INSERT INTO users (email, full_name, plan_tier, created_at)3VALUES (4 {{ emailInput.value }},5 {{ nameInput.value }},6 {{ planSelect.value || 'free' }},7 NOW()8)9RETURNING id, email, full_name, plan_tier, created_at;Pro tip: Use RETURNING in INSERT and UPDATE queries to get the created or modified row back from PostgreSQL — you can use this to update UI components immediately without re-running the full SELECT query.
Expected result: A complete CRUD interface works: clicking Add User opens the insert form, submitting creates a new row in PostgreSQL, clicking Delete with a row selected shows a confirmation dialog and then removes the record.
Use PostgreSQL advanced features: CTEs, JSONB, and window functions
Retool's query editor supports the full PostgreSQL SQL dialect, including advanced features that are unavailable in MySQL or simpler databases. Common Table Expressions (CTEs) let you break complex analytical queries into readable named steps. Write a WITH clause before the main SELECT to define intermediate result sets. Window functions (ROW_NUMBER(), RANK(), LAG(), LEAD(), running totals) are written directly in the SELECT list and work without any special Retool configuration. JSONB columns are queried using PostgreSQL's JSONB operators: ->> for text extraction, -> for JSON extraction, @> for containment queries, and jsonb_build_object() for constructing JSON. For full-text search, use to_tsvector() and to_tsquery() or the pg_trgm extension with ILIKE for similarity matching. These features are particularly powerful when building analytics dashboards, since Retool can pass complex SQL results directly to Chart components without any client-side transformation. The Chart component reads column names from the query result automatically — name your SELECT aliases clearly (monthly_revenue, user_count, cohort_month) so Chart's axis configuration is intuitive.
1-- CTE with window function example: monthly revenue with running total2WITH monthly_revenue AS (3 SELECT4 DATE_TRUNC('month', created_at) AS month,5 COUNT(*) AS new_orders,6 SUM(amount_cents) / 100.0 AS revenue7 FROM orders8 WHERE9 created_at >= {{ dateRangePicker.value[0] }}10 AND created_at < {{ dateRangePicker.value[1] }}11 AND status = 'completed'12 GROUP BY DATE_TRUNC('month', created_at)13)14SELECT15 month,16 new_orders,17 revenue,18 SUM(revenue) OVER (ORDER BY month ROWS UNBOUNDED PRECEDING) AS running_total,19 LAG(revenue) OVER (ORDER BY month) AS prev_month_revenue,20 ROUND(21 (revenue - LAG(revenue) OVER (ORDER BY month)) /22 NULLIF(LAG(revenue) OVER (ORDER BY month), 0) * 100,23 124 ) AS month_over_month_pct25FROM monthly_revenue26ORDER BY month;2728-- JSONB query example: filter users by metadata field29SELECT id, email, metadata->>'company' AS company30FROM users31WHERE metadata @> {{ JSON.stringify({plan: planFilter.value}) }}::jsonb32 AND metadata->>'country' = {{ countryFilter.value }};Pro tip: For complex analytical queries that take more than a few seconds, enable query caching in Retool (Advanced tab) with a duration of 5-15 minutes. This prevents the database from running expensive queries on every page load or component interaction.
Expected result: Advanced PostgreSQL queries — CTEs, window functions, JSONB operators — run successfully in Retool's query editor and return results that bind directly to Chart and Table components for visualization.
Common use cases
Application database admin panel
Build a full CRUD admin panel over your application's PostgreSQL database. Operations teams can search and filter records using a combination of text inputs and dropdowns that parameterize the WHERE clause, edit fields inline in the Table, insert new records through a Form, and delete records with a confirmation modal. All write operations use GUI mode or parameterized queries to prevent accidental mass updates.
Build a Retool admin panel for a PostgreSQL 'users' table. Show users in a Table with columns for id, email, name, plan, and created_at. Add a search input that filters by email. Include an Add User button that opens a Form to insert a new row, an Edit button for the selected row, and a Delete button with a confirmation dialog.
Copy this prompt to try it in Retool
Business metrics and reporting dashboard
Create a Retool dashboard that runs analytical SQL queries against your production PostgreSQL database — monthly revenue totals using window functions, user cohort retention using CTEs, or funnel conversion rates using subqueries. The dashboard combines Chart components for trends and Table components for detailed breakdowns, giving business stakeholders a self-serve view of key metrics without requiring SQL expertise.
Build a Retool business dashboard that shows monthly new user signups and revenue for the last 12 months using a PostgreSQL query with date_trunc('month', created_at) grouping. Display the results in a bar Chart and a summary Table. Include a DateRangePicker to adjust the time window.
Copy this prompt to try it in Retool
Data audit and remediation tool
Build a Retool tool for data quality teams to identify and fix data integrity issues in a PostgreSQL database — orphaned records, invalid email formats, null values where values are required, or duplicate entries. Each audit query surfaces problematic rows in a Table, and action buttons trigger parameterized UPDATE queries to apply standardized fixes, with a full log of changes for audit trail purposes.
Build a Retool data audit tool that runs a PostgreSQL query to find users with invalid email formats using a regex check. Show the results in a Table with a Fix button on each row that updates the record's email to a corrected value from an input field.
Copy this prompt to try it in Retool
Troubleshooting
Connection refused or 'could not connect to server' error
Cause: Either the PostgreSQL server is not accepting connections on the configured host and port, or a firewall is blocking Retool's IP addresses from reaching the database.
Solution: Verify the host and port are correct. For Retool Cloud, add Retool's CIDR ranges to your database server's firewall rules or security groups (35.90.103.132/30 and 44.208.168.68/30 for us-west-2). For AWS RDS, update the security group's inbound rules. For databases requiring SSH tunneling, enable it in the resource configuration.
Query returns 'permission denied for table' or 'relation does not exist'
Cause: The database user configured in the Resource does not have SELECT (or INSERT/UPDATE/DELETE) privileges on the target table, or the table name includes a schema prefix that is not in the user's search_path.
Solution: In your PostgreSQL server, grant the required privileges: GRANT SELECT, INSERT, UPDATE, DELETE ON tablename TO retool_user. For tables in non-public schemas, either use the fully qualified name (schema.tablename) in queries or update the user's search_path: ALTER USER retool_user SET search_path TO myschema, public.
1-- Grant read/write access on specific tables2GRANT SELECT, INSERT, UPDATE, DELETE ON users, orders, products TO retool_user;34-- Or grant on all tables in a schema5GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO retool_user;Parameterized query fails with 'operator does not exist: integer = text' type error
Cause: Retool's {{ }} parameters always pass values as text strings, and PostgreSQL's prepared statements enforce type matching. Integer or UUID columns reject string comparisons without explicit casting.
Solution: Add an explicit CAST in the SQL to convert the parameter to the correct type: WHERE id = {{ table1.selectedRow.id }}::integer or WHERE id = CAST({{ userId.value }} AS integer). For UUIDs, use ::uuid. For dates, use ::date or ::timestamptz.
1-- Cast parameters to match column types2SELECT * FROM orders WHERE id = {{ orderIdInput.value }}::integer;3SELECT * FROM users WHERE uuid_col = {{ uuidInput.value }}::uuid;4SELECT * FROM events WHERE event_date >= {{ dateInput.value }}::date;SSL connection error: 'SSL SYSCALL error: EOF detected'
Cause: The PostgreSQL server requires SSL but the resource is not configured with SSL mode, or the server's SSL certificate is self-signed and certificate verification is failing.
Solution: In the PostgreSQL Resource settings, change SSL Mode from disable to require (for basic SSL without certificate verification) or verify-ca/verify-full (with certificate verification). If the server uses a self-signed certificate, enable the 'Use a self-signed certificate' toggle in Retool's resource configuration.
Best practices
- Create a dedicated PostgreSQL user for Retool with only the privileges needed (SELECT, INSERT, UPDATE, DELETE on specific tables) rather than using a superuser account — this limits the blast radius of any accidental query.
- Use Retool's GUI mode for all INSERT, UPDATE, and DELETE operations in production apps — GUI mode validates inputs and shows a preview of the generated SQL before executing, reducing the risk of accidental data modification.
- Always include a WHERE clause in UPDATE and DELETE queries — consider enabling Retool's administrator setting that restricts raw SQL to SELECT-only if your team should not be running destructive SQL.
- Enable query caching (5-30 seconds) on frequently-run SELECT queries to reduce database load, especially for dashboards viewed by multiple users simultaneously.
- Use parameterized queries ({{ }} syntax) for all user-supplied values — Retool converts these to prepared statement parameters automatically, preventing SQL injection. Never concatenate user input directly into SQL strings.
- For databases in private VPCs or behind firewalls, use Retool's SSH tunnel feature rather than opening a direct internet-accessible port — SSH tunneling keeps your database off the public internet entirely.
- Name your SQL query result column aliases descriptively (monthly_revenue, user_email, order_count) — these names appear in Chart axis selectors and Table column headers, making the UI easier to configure.
- Store PostgreSQL connection credentials in Retool configuration variables marked as secret rather than hardcoding them in resource settings — this enables credential rotation without downtime.
Alternatives
MySQL uses identical Retool Resource configuration and is preferred for teams already on LAMP stack or running MySQL-based applications, though it lacks PostgreSQL's JSONB, CTEs, and advanced window function support.
Microsoft SQL Server connects via its own native Resource and is the preferred choice for teams in Microsoft-heavy environments or running .NET applications with T-SQL stored procedures.
Oracle Database has its own native Resource in Retool and is used by enterprises with existing Oracle infrastructure, though PostgreSQL has largely replaced Oracle for new projects due to cost and licensing.
Frequently asked questions
Is it safe to connect Retool to a production PostgreSQL database?
Yes, with appropriate safeguards. Create a dedicated database user for Retool with only the necessary privileges (SELECT for read-only apps, plus INSERT/UPDATE/DELETE for admin panels). Retool runs all queries as server-side prepared statements, which prevents SQL injection. Use Retool's app permission system to control which team members can access the production database connection. For destructive operations, add Confirm dialogs and consider enabling Retool's setting that restricts raw SQL to read-only.
Does Retool work with PostgreSQL on Supabase, Neon, or other managed providers?
Yes. Retool connects to any PostgreSQL-compatible endpoint regardless of the hosting provider. For Supabase, use the connection string from the Supabase dashboard (Settings → Database → Connection string → URI mode) and enable SSL mode require. For Neon, use the pooled connection string with SSL required. Both providers are standard PostgreSQL and all Retool features (SQL queries, GUI mode, JSONB) work identically.
Can I use PostgreSQL stored procedures and functions from Retool?
Yes. Call stored procedures with CALL procedure_name({{ param1 }}, {{ param2 }}) and functions with SELECT function_name({{ param }}) or SELECT * FROM function_name({{ param }}) for set-returning functions. The results bind to Table and Chart components like any other query. Custom functions that return complex types or composite rows return as standard JSON-serializable result sets in Retool.
How does Retool handle PostgreSQL connection pooling?
Retool manages connection pooling automatically for the PostgreSQL Resource — you do not need to configure PgBouncer or similar tools on the Retool side. Retool Cloud maintains a configurable pool size per resource (default is typically 5-10 connections). For databases with strict connection limits (like small Heroku Postgres tiers), consider setting a lower pool size in the resource's Advanced settings or using an external connection pooler like PgBouncer that Retool connects through.
Can I use Retool with PostgreSQL schemas other than public?
Yes. Reference non-public schema tables using the fully qualified name in SQL: SELECT * FROM analytics.events or UPDATE finance.transactions SET .... Alternatively, set the database user's search_path to include your schema (ALTER USER retool_user SET search_path TO analytics, finance, public) so you can reference tables without the schema prefix. The GUI mode also works with non-public schemas when you specify the full table name.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation