JavaScript in Retool
A quick breakdown of important JavaScript methods for building in Retool
Because JavaScript is such an integral part in building in Retool, we'll be going over specific JavaScript concepts that are particularly important in Retool. If you're brand new to JavaScript/programming in general, there are a few external resources that can help you get started.
- Codecademy—Learn JavaScript (Full curriculum)
- learn-js—Learn JavaScript (Simple docs)
There's a lot of overlap between JavaScript Basics and JavaScript in Retool, so if you're already familiar with the basics, feel free to skip this section and jump straight to JavaScript in Retool!
You can think of programming at a high level as just: manipulating data with logic. Retool is all about showing and manipulating your data with drag-and-drop buttons, tables, maps, etc.
So first, we'll cover data in general, how you can represent data, and then how you write logic to show and manipulate that data in Retool.
Data types
Everything in JavaScript is an object, which means it's just a thing with some properties (like a length) and some built-in methods that can manipulate this thing.
JavaScript object vs JavaScript Object
In JavaScript, essentially all data is referred to as an object. Within this broad category, there is a specific Object data type, which we'll learn about next.
To avoid confusion, I'm going to capitalize Object when we're referring to the specific data type that looks like this
{name: "Sparta", age: 9}
, rather than the general term object which broadly refers to data in JavaScript.
JavaScript has the following common data types, and all are valid in Retool.
Data type | Description | Example |
---|---|---|
String | A value surrounded by quotes. If a number is surrounded by quotes, it's a string. | "hi there" , "5" or "true" |
Number | A number value. Numbers are also not surrounded by quotes. | 5 |
Boolean | A true or false value. true and false are reserved keywords that don't need to be (and shouldn't be) surrounded in quotes. | true |
Array | A list-like, ordered data structure that can store multiple data types surrounded by hard brackets. | [5, 'hi', [1, 0]] |
Object | An un-ordered data structure with key:value pairs surrounded by curly braces and separated by commas. | {name: "Sparta", age: 9, good_dog: true} |
Data type | Description | Example |
---|---|---|
JSON | JavaScript Object Notation is a data format with properties but no dedicated methods. It's a compact text-based format that is surrounded by curly brackets and contains double quoted strings for keys and values. Single quotes are not valid. Common data type when working with APIs. | { "employees": [{"first_name":"Dwight", "last_name":"Schrute"}, {"first_name":"Pam", "last_name":"Beasley"}] } |
Data Conversion
Managing data types is a fundamental concept that comes up fairly often when manipulating data in Retool. Let's say you want to get the sum of all the prices in your inventory, but the price column is stored as a string in your database. You can use JavaScript data conversion methods to turn those strings into sum-able integers!
Method | Description | Example | Result |
---|---|---|---|
parseInt("") | String → Integer | {{ parseInt("3") }} | 3 |
number.toString() | Number (integer, float/decimal, etc.) → String | {{ 3.5.toString() }} | "3.5" |
['3.5', '3'].map(Number) | Array of strings → Array of integers | {{ ['3.5', '3', 8].map(Number) }} | [3.5,3,8] |
Truthy vs Falsy
Because of JavaScript's under-the-hood type casting, all data is either true or false. Numbers can be true or false, words/strings can be true or false. Since true and false are reserved keywords in JavaScript that refer directly to the Boolean type true
or false
, we say "truthy" or "falsy" as the category that data can fall under. Everything is truthy, except for the following 7 falsy values:
- the boolean
false
- the keyword
null
- the keyword
undefined
- the numeric data type
NaN
- the empty string
""
or empty array[]
- the number
0
- the BigInt
0n
Q: Is "0"
truthy or falsy?
A: Truthy. It's a non-empty string (truthy).
Q: Is -0
truthy or falsy?
A: Falsy. It evaluates to just 0 (falsy).
Q: Is "false"
truthy or falsy?
A: Truthy. It's a non-empty string (truthy).
More on truthy vs falsy in JavaScript here.
Operators
A comparison operator compares its operands (the values before and after the operator) and returns true if the statement is truthy, or false if the statement is falsy.
Operator | Name | Example | Result |
---|---|---|---|
> | Greater than | {{ 1 > 1 }} | false |
< | Less than | {{ 1 < 1 }} | false |
>= | Greater than or equal | {{ 1 >= 1 }} | true |
>= | Less than or equal | {{ 1 <= 1 }} | true |
Logical operators will also return a boolean depending on the truthiness (or falsiness) of the statement, with the exception of the ||
and &&
operators which can return a non-boolean, depending on the operands.
Operator | Name | Description | Example | Result |
---|---|---|---|---|
\|\| | OR | x \|\| y Returns x if truthy or else returns y . When used with Boolean values it returns true if either operand is true or false if both are false. | {{ true \|\| false }} {{ 0 \|\| 7 }} | true 7 |
&& | AND | x && y Returns x if falsy or else y . When used with Boolean values it returns true if both operands are true or false if one is false. | {{ true && false }} {{ 0 && 7 }} | false 0 |
! | NOT | !x Returns a Boolean of the reversed truthiness/falsiness of x . | {{ !true }} | false |
== | Equals | x == y Returns true if the expressions match or false if they don't. Data type is not considered. | {{ 1 == '1' }} | true |
=== | Strict equals | x === y Returns true if the expressions match, false if they don't. Data type is considered. | {{ 1 === '1' }} | false |
!= | Does not equal | x != y Returns true if the expressions don't match, false if they do. The type of data is not considered. | {{ 1 != '1' }} | false |
!== | Strict inequals | x !== y Returns true if the expressions don't match, false if they do. Data type is considered. | {{ 1 !== '1' }} | true |
JSON and Data
There are two main concepts at play here: accessing data in an Object and accessing data in an array.
Then, in the JavaScript in Retool section, we can begin thinking about accessing data in an Object in an array in an Object. This is referred to as nested data, which is how Retool queries to your database or API will return data.
Accessing data in an Object
Values in an Object can be accessed using either dot notation (object_name.key_name
) or bracket notation (object_name['key_name']
). When working with dot notation, property identifies can only be alphanumeric (and _ and $). Dot notation can be limiting (properties can't start with a number), but adding a . after an object will pull up a helpful autocomplete menu in Retool. More on that below!
Here's an example where we first define an Object with the name obj1
, then access the value held by the 'greeting'
key:
const obj1 = { 'greeting': ['hi', 'bye'], 'number': 5, 0: 1 }
return obj1['greeting'] // this returns ['hi', 'bye']
Retool will offer autocomplete options when writing JavaScript. If you have an Object (which also includes queries or components, like text1
) and aren't sure which columns, properties or methods you can access, type .
In this case, dot notation is better than bracket notation since a [
will not bring up the autocomplete menu.
Accessing data in an array
Values in an array can be accessed using the index
, or numeric location, of the target. The index always starts at 0, so the first element of an array can be accessed using array[0]
.
Here's an example:
const arr1 = ['dog', 'cat', 'frog']
return arr1[1] // this returns 'cat'
JavaScript in Retool
Data in Retool can be manipulated using JavaScript. Data objects have properties that can be referenced. For example, you can access a query's data property via query.data
, and a Text Input component's inputted value via textinput.value
and a Table component's selected row via table.selectedRow.data
.
Imagine you have a database. This database stores user information—id, name, email and so on. Once you write a SQL query to get this data from your database into Retool, how would you display this data?
You guessed it—JavaScript! You can use JavaScript on the query.data
value returned from the query you just ran. We're going to go over a few commonly used JavaScript methods in Retool that would help accomplish this.
In Retool, JavaScript can be written between {{ }}
or directly in the JavaScript query type, which we'll get into later. You can also reference the values of Retool components from within {{ }}
! More on pulling data from your components here.
JavaScript in between {{ }}
is limited in a few ways. To output a value from a set of {{ }}
, it needs to be a self executing function, method, or single value like {{ query1.data.map(row=>row.id) }}
or {{ query1.data.id.length }}
. You can't run any Retool-specific methods from inside them, like text1.setValue()
. (Those Retool-specific methods must be run from a JavaScript query, and complicated if-else statements are easier to manage inside of a Transformer, which we'll get into next.)
You can access and use the output from any query. For example, {{ getProducts.data }}
returns the following data where getProducts
is a SQL query to get data from a products
table in a database.
Here's what getProducts.data
looks like:
{
"id":["1", "2", "3"],
"name":["Stucture and Interpretation of Computer Programs","Godel, Escher, Bach","The Seasoned Schemer"],
"quantity":[998001,77777,12563]
}
You can then use {{ getProducts.data }}
in a Table Component to display it!
The most common example of {{ }}
in action:
Ternaries
Since you can't write if else logic in between {{ }}
, but you want to conditionally return data, you can use ternaries. Ternaries are a one-line version of an if else statement (MDN docs here).
condition
? execute if condition is true
: execute if condition is false
Since everything in JavaScript is either truthy or falsy, the condition section can be a statement with a comparison operator (e.g. === or >=), or just a plain old object (e.g. table1.data
or checkbox1.value
). If table1.data
exists (has data, and is not empty), the condition will evaluate as true and the first action will execute. See example below!
JavaScript Queries
JavaScript queries are a special query type in Retool that allow you to write multi-line JavaScript, trigger other queries, download data from your app, set temporary state to store data in your current browser session, etc. Go ahead and read our JavaScript query docs on all the cool things they can do!
One thing you can't do is set table data manually in a JavaScript query. You can return some data, then use the JavaScript query's data in the table's value field, but table1.data = something
won't work, nor will something like table1.hidden = true
. Components in Retool are generally read only, and are only modifiable with specific methods, found in the Scripting Retool docs linked above (and here).
Quick note: for security reasons, all JavaScript runs in a sandbox.
If JavaScript ran directly on your page, other people in your org could inject malicious scripts to end users, including yourself. To prevent that, we execute all JavaScript in a separate iframe, on a different domain.
That means that inside of your preloaded JavaScript, you won't be able to use jQuery, or other hosted libraries to create your own components, listen to events on the Retool page, etc. You can, however, import libraries! Here is a section of our docs that walk through it. You would need to add it to the libraries section in settings > advanced, then you should be able to get access to the library's methods.
Queries and transformers
Queries are best for calling JavaScript methods (ie query.trigger()
, state1.setValue()
, etc) while transformers are best for returning a single value or data object.
Queries | Transformers | |
---|---|---|
Format of Retool data (other queries, components, etc) to be used in the query | textinput1.value | {{ textinput1.value }} |
Runs… | …when manually triggered | …on input change + page load |
Executable Retool-specific methods | - query.trigger() - state1.setValue() - table.selectRow(index) - modal.open() - utils.downloadFile(data, fileName, fileType) - More methods here! | 🚫 |
Return value | Doesn't need a return value, can just be used to run methods listed above | Must have a return value |
How can I access the returned value? | {{ query.data }} | {{ transformer.value }} |
Viewing your data
We're going to take a quick detour and take a look at the ways you can view your data before we get into accessing nested, and more complicated data structures. Viewing your data gives you a birds-eye view of the maze that is nested data, and therefore, makes it easier to access the values you're looking for.
To recap from the beginning, once you successfully query your database, you'll get data back and into your Retool app. This data can be accessed in the data property of the query, like
{{ getPeople.data }}
. {{ getPeople.data }}
is a nested Object (an Object with Objects with arrays inside).
Retool offers 3 main ways to view this query data: the Query Preview, the Left Panel and the (green) Value Preview.
Query Preview | Left Panel | Value Preview | |
---|---|---|---|
Format | Table | Expandable/interactive | Raw |
Data types indicated? | 🚫 | ✅ | ✅ |
Location | Below the query after clicking "Preview" | Left Panel | Below a value input after clicking in said input |
Example | ![]() ![]() | ![]() ![]() | ![]() ![]() |
- Query Preview
The Preview option will show your data formatted as a table, with easy to view headers/column names and values.
- Left Panel (also named the Model Browser)
The Left Panel has information about all of your query, component, transformer, temporary state, global variables (ie current_user or urlparams) data in Retool. You can expand out the object you're interested in to learn more about its data structure.
Each object (in bold) will have its type next to it (in gray) like {}
or []
, as well as its length in number of items or number of keys.
Each key represents different properties of a query, like error
to indicate the presence of an error or isFetching
to let you know if the query is still running. It also has the data
key, the most important key for queries.
- Value preview
This preview shows the raw data. You can even copy the value to your clipboard.
Again, all 3 screenshots are showing getUsers.data
, just presented in different ways. Now let's learn how to actually access, and use, this data!
Accessing nested data
This is one of the most important concepts in Retool. We'll learn how to access specific data returned from your database (via queries) so you can start using that data in your apps.
In the example below, the getPeople
query has the data
key/property (the syntax is interchangeable here), which has the keys first
, id
and last
, which are the column names from our people
table. The first
key points to an array of first name strings.
{{ getPeople.data }}
returns the following data. Again, it's the same as the data we see above in the Left Panel screenshot, just presented differently.
{
"id":[1,2,3,4,5,6,7,8,9,10],
"first":["Myrtle","Nellie","Theodore","Randall","Ralph","Travis","Randy","Christopher","Eula","Jane"],
"last":["Barber","Bowman","Phelps","Hale","Wise","Daniel","Harrison","Allen","Austin","Lewis"]
}
Let's say we want to access the array of ids to use in a dropdown to allow end users to select the user they'd like more info on. To access the array of ids, we can use {{ getPeople.data.id }}
.
Data Conversion
There are two data conversion methods that are special to Retool:
formatDataAsArray
and formatDataAsObject
.
Data from SQL queries are returned as an Object of arrays, where each key is a column name that points to an array of column values.
If you want to show the data as an array of Objects instead (where each Object represents a row), you can use the helper function formatDataAsArray
like so: {{formatDataAsArray(sqlQuery.data)}}
Method | Description | Retool use case |
---|---|---|
formatDataAsArray | Object of arrays → Array of Objects In the context of Retool, it will convert an Object of table column arrays to an array of row Objects. | getActors.data :![]() ![]() {{ formatDataAsArray(getActors.data) }} :![]() ![]() |
formatDataAsObject | Array of Objects → Object of arrays In the context of Retool, it will convert an array of row Objects to an Object of table column arrays. | Reverse of above |
Array methods
Similarly, whether you need to find the row of data based on the id selected from a Dropdown component, or add new values to the array stored in Retool's temp state (docs here), you'll need to use JavaScript!
Method | Description | Example | Retool use case |
---|---|---|---|
.length | Returns the length of an array | ![]() ![]() | getItems.data.length returns the total number of items returned from the query |
.join | Combines elements from an array into a string, with an optional separator (commonly a space).join 's reverse function is .split , MDN docs here and here | ![]() ![]() ![]() ![]() | |
.indexOf | Returns the position of the first occurrence of the given value | ![]() ![]() | |
.includes | Returns true or false if an array includes the value passed into the method | ![]() ![]() | getItems.data.id.includes("1") returns true if an item with id of 1 exists in the items returned from the query |
.forEach | Loop through each element in an array | ![]() ![]() | |
.filter | Returns a new array with the elements from the original array that match a specified condition | ![]() ![]() | |
.map | Returns a new array with the results of running a specified function on each element of the original array | ![]() ![]() ![]() ![]() | getItems.data.id.map(Number) returns an array of the ids (originally strings) turned into numbers |
.push | Add an item to the end of an array | [7, 8, 9].push(10) // 4 Note: [7, 8, 9].push(10) will return the new length of the array, but it will still make the change "in the background"![]() ![]() | |
.concat | Merge 2 or more arrays | ![]() ![]() | ![]() ![]() Let's add some values to our state! const newValues = ['cat', 'kitten'] state1.setValue(state1.value.concat(newValues)) Now our state looks like this: ![]() ![]() |
.reduce | Reduces the array to a single value by running a provided function for each value of the array (from left-to-right) | ![]() ![]() Where getOrders.data.charge_total is an array of numbers | Useful to sum all values in an array, and can be done in between {{ }} without needing to use a JavaScript query or transformer |
Dates
When dealing with dates in Retool, you'll most likely want to use moment()
. You can use it anywhere you can use JavaScript, like so: {{ moment() }}
. Moment is an awesome, external JavaScript library that helps you manage dates in the browser that comes pre-installed in Retool, and has its own docs here.
Most SQL databases store dates in a date
or datetime
type column, so if you have a string
date (like '12/25/1995'), your database will likely reject it. You'll need to convert the string
into a proper date
type before sending it to your database; this is where Moment comes in.
Let's go over a few of the most relevant Moment methods!
Method | Description | Example |
---|---|---|
moment() | Returns the current datetime in the users current timezone | ![]() ![]() |
moment('date string', 'new format') | Converts a date string in the given format to a date object | ![]() ![]() |
.format('new format') | Formats your current date. See docs for full table on acceptable date format options (e.g. dddd, Do, etc.) | moment().format(); // "2014-09-08T08:02:17-05:00" moment().format("MMMM Do YYYY, h:mm:ss a"); // "February 14th 2010, 3:25:50 pm" moment().format("ddd, hA"); // "Sun, 3PM" moment().format("[Today is] dddd"); // "Today is Sunday" moment('gibberish').format('YYYY MM DD'); // "Invalid date" ![]() ![]() |
.unix() | Outputs a Unix timestamp | ![]() ![]() |
.isBefore('second date') | Returns a boolean if first given date is before the second given date | ![]() ![]() |
.isAfter('second date') | Returns a boolean if first given date is after the second given date | ![]() ![]() |
.add(duration, 'duration type') | Adds a given duration and duration type | moment() —today![]() ![]() moment().add(7, 'days') —today + 7 days![]() ![]() |
.subtract(duration, 'duration type') | Subtracts a given duration and duration type (I.E. 'days', 'hours) | moment() —today![]() ![]() moment().subtract(7, 'days') —today - 7 days![]() ![]() |
.diff(moment, string) | Returns the difference between two moments, as the specified string duration (I.E. 'days', 'hours') | ![]() ![]() |
.tz('timezone') | Changes the timezone | ![]() ![]() |
.utc() | moment("date", "current format").utc() returns the given date in UTC. moment.utc("date", "current format") will not convert to UTC. It's only setting your input to UTC time. It's basically a way of indicating that the time you input is in UTC. Retool's preview is in UTC | ![]() ![]() ![]() ![]() ![]() ![]() |
Updated 11 days ago