# Retool

> Build internal tools and admin panels fast by dragging components onto real data sources, wiring queries and state - then ship them with auth and deployment that hold up.


---

# Retool

Every company ends up needing a pile of small, unglamorous apps: a page to refund a customer, a dashboard to see today's orders, a form to approve a vendor, a screen to flip a feature flag. None of these are products. They're internal tools - the duct tape that keeps operations running. Building each one from scratch with a real front-end framework is slow, and nobody on the team wants to maintain ten half-finished React apps. Retool exists for exactly this gap: it lets you drag tables, forms, and buttons onto a canvas, point them at your actual database or API, and have a working tool in an afternoon instead of a sprint.

This guide is for founders, ops people, support leads, and analysts who have real data sitting in a database or behind an API and need a usable interface on top of it - without hiring a front-end team for it. You don't need to be a programmer, though a little comfort with reading SQL and the occasional one-line expression goes a long way. We assume you're new to Retool specifically, not to the problem it solves.

The three phases build on each other. Phase 1 covers the core mental model: what an internal tool actually is, the component-on-canvas way Retool works, and how to connect it to a database or API so you're looking at live data. Phase 2 is where the tool comes alive - writing queries, binding components to data and to each other, transforming values, and handling what happens when someone clicks a button. Phase 3 is the part people skip and regret: locking down who can see and do what, deciding between Retool's cloud and self-hosting, getting changes through review safely, and recognizing the places where Retool gets awkward so you can plan around them instead of fighting them at 2am.


---

# Internal Tools, Fast

Picture the work your team does that never makes it into the product. Support needs to look up a customer and issue a refund. Ops needs to see which orders shipped late. Finance needs to approve invoices over a threshold. None of this is customer-facing. It's all the same shape underneath: read some rows, show them in a table, let a human change one, write it back. That shape has a name in the trade - CRUD, for Create, Read, Update, Delete. Internal tools are CRUD interfaces on your own data.

The reason these are painful to build from scratch is that the boring parts dominate. A real web app means a front-end framework, a build pipeline, a hosting setup, auth, and a back-end to talk to your database safely. For a page two people use twice a day, that's a wildly bad trade. So companies either over-build (a neglected mini-app per task) or under-build (everyone shares a database password and edits rows by hand, which goes wrong eventually). Retool sits in the middle: it gives you the front-end and the safe connection to your data, so the only thing left to decide is what the screen should do.

## The canvas-and-components model

A Retool app is a canvas you drop components onto. Components are the building blocks of the interface - the visible parts a user sees and touches. The catalog is large, but you'll live in a handful:

| Component | What it's for |
|---|---|
| Table | The workhorse. Shows rows of data, sortable and filterable. |
| Text Input / Number Input | Capture a value to search or save. |
| Select / Dropdown | Pick from a fixed list. |
| Button | Trigger an action - save, refresh, delete. |
| Form | A group of inputs that submit together. |
| Text | Labels, headings, read-only values. |
| Modal | A pop-up for confirmation or a detail view. |

You drag these from a side panel onto a grid. They snap into place and resize by dragging. Each component has an inspector panel where you set its properties - what a table shows, what a button says, what happens on click. The key mental shift: a component is not static. Its content can come from data, and its data can come from another component. A table's rows come from a query; a query's filter comes from a search box; a detail panel shows whichever row is selected in the table. Components reference each other by name, and Retool keeps everything in sync as values change. That live wiring is the whole game, and it's where Phase 2 goes deep.

For now, the takeaway is the layout-first feeling: you build the screen you want a human to use, then attach data to it. You're not writing an app, you're assembling one.

## Connecting to your data

A canvas with no data is a mockup. The thing that makes Retool useful is the resource - Retool's word for a connection to a data source. You set up a resource once, then any app can use it. Resources come in two broad families.

The first is databases. Retool connects to the usual suspects - PostgreSQL, MySQL, Microsoft SQL Server, plus warehouses like Snowflake and BigQuery, and many more. You give it the host, port, database name, and credentials, and it stores that connection on Retool's side. From then on, your apps talk to "Production DB" by name and never see the raw password. Inside a database resource you write SQL queries (or use a guided GUI mode for plain reads and writes if SQL isn't your thing).

The second is APIs. If your data lives behind an HTTP service - your own back-end, Stripe, a CRM, an internal microservice - you set up a REST or GraphQL resource with the base URL and auth (an API key, a bearer token, OAuth). Retool also ships pre-built integrations for many common SaaS tools so you don't wire the auth by hand.

```text
Resource (set up once)
  name:     Production DB
  type:     PostgreSQL
  host:     db.internal.example.com
  database: app_production
  creds:    stored on Retool's side, not in the app

App "Refund Tool"  ──uses──▶  Resource "Production DB"
App "Order Board"  ──uses──▶  Resource "Production DB"
```

One detail worth internalizing now, because it shapes everything: where the connection lives. On Retool's cloud, the resource connection runs from Retool's servers, so your database has to be reachable from the internet (or via an allowlist of Retool's IPs, or a tunnel). Many teams aren't comfortable opening their production database that way, which is the main reason they self-host Retool - we cover that trade in Phase 3. For your first tool, a read-only connection to a non-critical database is the safe way to learn.

## The first-tool arc

Here's the shape of building your first real tool, before any wiring detail:

```text
1. Add a resource for your database or API.
2. Create a new app, drop a Table on the canvas.
3. Write one query that reads rows; point the table at it.
4. Add a search input; feed it into the query's filter.
5. Add a button that writes a change back.
```

Steps 1 and 2 you've seen. Steps 3 through 5 - turning a static table into something that reads, filters, and writes live - are the craft of Retool, and that's exactly where Phase 2 picks up.


---

# Wiring Data, Queries & State

A table sitting on a canvas does nothing until you wire it up. Wiring is where Retool stops being a layout tool and starts being an app. There are four moving parts: queries that move data in and out, bindings that connect components to data and to each other, transformers that reshape values, and events that decide what happens when someone clicks. Get these four straight and you can build almost anything Retool is good at.

## Queries: the data verbs

A query is a named operation against a resource. It's the only way data enters or leaves your app. There are read queries and write queries, and the difference matters.

A read query pulls rows in - `select * from orders where status = 'late'`. A write query changes something - an insert, an update, a delete, or an API call that posts data. Each query has a name (`getOrders`, `updateRefund`), and that name becomes a handle you reference everywhere else. After a read query runs, its results live at `getOrders.data`, and any component can point at that.

The most useful thing about queries is that they take inputs from the rest of the app using a binding - an expression wrapped in double curly braces. Inside the braces you write a small JavaScript expression that reads other parts of the app.

```text
-- A query named getOrders, on the Production DB resource
select *
from orders
where status = {{ statusSelect.value }}
  and created_at > {{ dateInput.value }}
limit 100
```

Here `statusSelect` and `dateInput` are components on the canvas. When the user changes the dropdown, the binding re-evaluates and (if you set the query to run automatically) the table refreshes. That's the live feel from Phase 1, made concrete.

A safety note that's not optional: those `{{ }}` values are passed as bound parameters, not pasted into the SQL string. That's what keeps a user typing into a search box from rewriting your query - the database treats their input as a value, never as code. Use the binding; never hand-concatenate user input into a query string.

Queries don't only run on a schedule or a change. You can run one on demand - most often from a button - which we get to under events.

## Binding: components reading each other

Anywhere you see a property field with the `{{ }}` syntax available, you can put an expression that reads the app's current state. This is how components reference each other.

```text
Table  "ordersTable"   →  Data:  {{ getOrders.data }}
Text   "selectedId"    →  Value: {{ ordersTable.selectedRow.id }}
Query  "getCustomer"   →  where id = {{ ordersTable.selectedRow.customer_id }}
Text   "customerName"  →  Value: {{ getCustomer.data[0].name }}
```

Read that chain top to bottom and you've built a master-detail view: a table of orders, and a panel that shows the customer for whichever row is selected - without a line of glue code. Each binding is a tiny expression Retool re-evaluates whenever anything it depends on changes. The mental model is a spreadsheet: cells reference other cells, and editing one ripples outward automatically.

Bindings can do light logic inline: `{{ ordersTable.selectedRow ? "Editing order " + ordersTable.selectedRow.id : "Pick a row" }}`. Keep these short. The moment an expression needs more than a line, move it into a transformer.

## Transformers: reshaping data

Real data is rarely the shape your UI wants. The API returns nested JSON; you need a flat list. Two queries return halves of the same picture; you need them joined. A status code of `2` should read "Shipped". A transformer is a named block of JavaScript that takes inputs and returns a value - a clean place to do that reshaping once and reference the result like any other value.

```text
transformer "ordersForTable":
  return {{ getOrders.data }}.map(o => ({
    id:       o.id,
    customer: o.customer_name,
    total:    "$" + (o.cents / 100).toFixed(2),
    status:   { 1: "Pending", 2: "Shipped", 3: "Late" }[o.status]
  }))
```

Now your table binds to `{{ ordersForTable.value }}` and shows friendly columns, while the raw query stays untouched. Transformers recompute automatically when their inputs change, same as bindings. The rule of thumb: bindings for one-liners, transformers for anything you'd be tempted to copy-paste or that's more than a line of logic.

## Events: making buttons do things

Components fire events - a button has a click event, an input has a change event, a table has a row-select event. You attach event handlers to them. An event handler is "when X happens, do Y." The common Ys:

- Run a query (`updateRefund.trigger()`)
- Show or hide a modal
- Set a variable
- Show a notification ("Refund saved")
- Navigate to another page

A real write flow chains these. The button's click handler runs the write query; the write query has its own success handler that refreshes the read query and pops a confirmation; on failure, it shows the error.

```text
Button "Save Refund"  on click  →  trigger query  updateRefund

Query "updateRefund"  on success →  trigger query   getOrders   (refresh the table)
                                 →  show notification "Refund saved"
                      on failure →  show notification {{ updateRefund.error }}
```

This is the part beginners under-do. A write that doesn't refresh the table leaves the user staring at stale data, convinced it didn't work. Always close the loop: write, refresh, confirm.

## App state: holding values between clicks

Sometimes you need to remember something that isn't in any component or query - a multi-step wizard's progress, a list of selected IDs for a bulk action, a toggle. For that, Retool gives you variables (also called state): named slots you create, read with `{{ myVar.value }}`, and write with a "set variable" action in an event handler. Reach for a variable only when a value genuinely needs to outlive a single interaction or be shared across components. Most of the time the data you need already lives in a query result or a component's value - check there first before inventing state.

Put the four parts together - queries move data, bindings connect it, transformers shape it, events drive it, and variables remember it - and you have the full toolkit. Phase 3 is about making the tool you built safe to put in front of your team.


---

# Auth, Deployment & Gotchas

A working tool and a safe tool are different things. The wiring from Phase 2 makes a screen do what you want; this phase is about making sure the right people use it, your data stays where it should, changes don't break things in front of users, and you don't get surprised by the spots where Retool fights back. This is the unglamorous half, and it's the half that decides whether your tool survives contact with a real team.

## Permissions: who can see and do what

The first question for any internal tool is who's allowed to touch it. A refund button is fine for support leads and a disaster for everyone else. Retool's model is users grouped into groups, with permissions granted to groups rather than to people one by one. You don't say "Maria can use the Refund Tool" - you put Maria in the Support group and grant the Support group access. New hire joins, you add them to the group, and they inherit everything.

Permissions apply at the app level (who can open this tool) and the resource level (which connections a group is even allowed to query). A common safe setup: an Analysts group that can open dashboards and only touches read-only resources, and an Admins group that can edit apps and reach write resources. There's also editor-versus-user - being able to use an app is separate from being able to change it. Most of your team should be users, not editors.

For sign-in, small teams use email-and-password or Google login. Once you're past a handful of people, you'll want SSO - single sign-on through your identity provider (Okta, Azure AD, Google Workspace) so access follows the same on/offboarding as everything else. SAML-based SSO and SCIM provisioning are paid-plan and self-hosted features, not on the free tier; if "access must be revoked the moment someone leaves" is a hard requirement, budget for it.

## Cloud vs self-hosted

This is the biggest structural decision, and it usually comes down to where your data is allowed to be reached from.

| | Retool Cloud | Self-Hosted |
|---|---|---|
| Who runs it | Retool | You (Docker / Kubernetes) |
| Your DB exposure | Reachable from Retool's servers | Stays inside your network |
| Maintenance | None | You patch and upgrade |
| Setup speed | Minutes | Hours to days |
| Best for | Speed, small teams, non-sensitive data | Strict data rules, internal-only databases |

On the cloud, Retool's servers make the connection to your database, so the database must be reachable from the internet or via a tunnel. For many teams that's a non-starter - production databases are supposed to be unreachable from outside. Self-hosting puts the whole Retool engine inside your own network (a container you run), so queries to your database never leave your perimeter. The price is that you now own the upgrades, the backups, and the 2am page when it's down. Pick cloud unless a data-residency or network rule forces your hand; pick self-hosted the moment one does.

## Shipping changes without breaking things

Early on, people edit the live app while colleagues are using it. That works until the day a half-finished change goes out mid-shift. Retool's answer is release versions: you save named versions of an app and choose which one users see, so you can edit freely and only promote a version when it's ready. Rolling back is selecting the previous version - fast, and worth knowing before you need it.

For teams that treat tools like real software, Retool supports Source Control - connecting an app's definition to Git so changes go through branches and review, mirroring your normal dev workflow. That's overkill for a two-person tool and exactly right for a tool a dozen people depend on. Either way, the principle holds: edit somewhere that isn't what your users are looking at, and promote deliberately.

## Where Retool gets awkward

Knowing the rough edges ahead of time saves you from discovering them under pressure.

- **Performance with big tables.** Dumping tens of thousands of rows into a table and filtering client-side gets sluggish. Filter and paginate in the query - `limit` and `where` on the database side - instead of fetching everything and sorting in the browser. This is the single most common reason a Retool app feels slow.
- **Too many auto-running queries.** Queries set to run on load or on every change can stampede - one input change triggering a cascade of refetches. Be deliberate about which queries run automatically versus on a button.
- **Logic sprawl.** It's tempting to scatter `{{ }}` expressions everywhere. Six months later, finding why a value is wrong means hunting through dozens of tiny bindings. Consolidate real logic into named transformers so there's one place to look.
- **Vendor lock-in.** Apps are defined in Retool's format. Self-hosting and Git export soften this, but you're not going to lift a Retool app into a plain React project. Fine for internal tools you'll keep in Retool; a real consideration if you suspect a tool will graduate into a customer-facing product.
- **It's not a product front-end.** Retool is built for internal users behind a login. It's not the tool for a public marketing site, a customer-facing signup flow, or anything that needs pixel-perfect branding and public scale. Use it for the back office, not the storefront.
- **Cost scales with seats.** Pricing is largely per-user. A tool used by your whole company is a different line item than a tool for five ops people. Know the model before you roll something out org-wide.

The plain summary: Retool is excellent at the thing it's for - internal CRUD tools on data you already have, built fast and maintained by a small team. Respect its boundaries, lock down access from day one, ship changes through versions rather than live edits, and keep your queries doing the heavy lifting on the database side. Do that, and the tools you build will outlast the afternoon you spent building them.
