[docs]: LTS 3.16 Version

This commit is contained in:
rudrapratik30 2025-08-04 12:19:49 +05:30
parent 59cf5f329a
commit 0ca1af58d2
401 changed files with 76110 additions and 2 deletions

View file

@ -261,10 +261,10 @@ module.exports = {
// Please change this to your repo.
editUrl: 'https://github.com/ToolJet/Tooljet/blob/develop/docs/',
includeCurrentVersion: true,
lastVersion: '3.5.0-LTS',
lastVersion: '3.16.0-LTS',
versions: {
current: {
label: '3.11.0-Beta 🚧',
label: 'Beta 🚧',
path: 'beta',
banner: 'none',
badge: false
@ -280,6 +280,10 @@ module.exports = {
"3.5.0-LTS": {
banner: 'none',
badge: false
},
"3.16.0-LTS": {
banner: 'none',
badge: false
}
}
},

View file

@ -0,0 +1,5 @@
{
"label": "Actions Reference",
"position": 7,
"collapsed": true
}

View file

@ -0,0 +1,19 @@
---
id: close-modal
title: Close modal
---
Use this action to close the modal that is already shown.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/closemodal/closemodal2.png" alt="ToolJet - Action reference - Close modal" width="700" />
</div>

View file

@ -0,0 +1,102 @@
---
id: control-component
title: Control component (Component Specific Actions)
---
Control component action invokes the component specific actions. Component specific actions are the actions that are exclusive actions for a particular component. Component specific actions can be triggered either through the event handlers or from the Run JavaScript code query.
You can find the component specific actions for the specific component in their respective documentation. For example, you can find the component specific actions for the **Bounded Box** component in the [Bounded Box](/docs/widgets/bounded-box) documentation.
<details id="tj-dropdown">
<summary>Currently, Component specific actions are supported only by the below listed components.</summary>
<div>
<ul>
<li><a href="/docs/widgets/button#component-specific-actions-csa">Button</a></li>
<li><a href="/docs/widgets/checkbox#component-specific-actions-csa">Checkbox</a></li>
<li><a href="/docs/widgets/color-picker#component-specific-actions-csa">Color Picker</a></li>
<li><a href="/docs/widgets/dropdown#component-specific-actions-csa">Dropdown</a></li>
<li><a href="/docs/widgets/file-picker#component-specific-actions-csa">File Picker</a></li>
<li><a href="/docs/widgets/form#component-specific-actions-csa">Form</a></li>
<li><a href="/docs/widgets/icon#component-specific-actions-csa">Icon</a></li>
<li><a href="/docs/widgets/kanban#component-specific-actions-csa">Kanban</a></li>
<li><a href="/docs/widgets/link#component-specific-actions-csa">Link</a></li>
<li><a href="/docs/widgets/map#component-specific-actions-csa">Map</a></li>
<li><a href="/docs/widgets/modal#component-specific-actions-csa">Modal</a></li>
<li><a href="/docs/widgets/multiselect#component-specific-actions-csa">Multiselect</a></li>
<li><a href="/docs/widgets/radio-button#component-specific-actions-csa">Radio button</a></li>
<li><a href="/docs/widgets/table#component-specific-actions-csa">Table</a></li>
<li><a href="/docs/widgets/tabs#component-specific-actions-csa">Tabs</a></li>
<li><a href="/docs/widgets/text-input#component-specific-actions-csa">Text Input</a></li>
<li><a href="/docs/widgets/text#component-specific-actions-csa">Text</a></li>
<li><a href="/docs/widgets/textarea#component-specific-actions-csa">Text Area</a></li>
</ul>
</div>
</details>
:::info
Check out the **[demo](https://youtu.be/JIhSH3YeM3E)** of Component specific actions demonstrated in one of our community call.
:::
## Using Component Specific Actions
### Set a value for text input component using button's event handler
- Drag a **Text Input** and a **Button** component onto the canvas.
- Go to the **Inspector** on the left sidebar to check the exposed variables available for the `textinput1` component under the `components`. You'll see that the variable `value` is an empty string because the field value of the text input component is empty right now.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/inspector.png)
</div>
- Now enter some value in the text input component and you'll see that the `value` in inspector has been updated.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/updated.png)
</div>
- Now, click on the button's component handler to open up its properties in the right sidebar and then add a event handler for **On Click** event to trigger **Control Component** action. Select `textinput1` in component dropdown, `Set text` as Action, and in `Text` field enter the text that you want to update in the field value.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/button.png)
</div>
- Now when you'll click on the button you'll see that the field value of the text input component has been updated with value that you set.
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/set.png)
</div>
### Clear value of text input component using JavaScript query
- Let's clear the value that we set in the previous section, using Run JavaScript code. Create a new Run JavaScript Code query and call the component and the CSA that component provides.
**Syntax:**
```js
await components.textinput1.clear()
```
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/jsoption.png)
</div>
<div style={{textAlign: 'center'}}>
![ToolJet - Action reference - Control Component](/img/actions/controlcomponent/clear.png)
</div>
- Finally, hit the **save and run** query button to fire up the query, and you'll see that the field value of the text input component has been cleared.

View file

@ -0,0 +1,18 @@
---
id: copy-to-clipboard
title: Copy to clipboard
---
Use this action to copy the text to the clipboard.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/copytoclipboard/copy2.png" alt="ToolJet - Action reference - Copy to clipboard" width="700" />
</div>

View file

@ -0,0 +1,56 @@
---
id: generate-file
title: Generate file
---
# Generate file
This action allows you to construct files on the fly and let users download it.
## Options
| Option | Description |
|--------|-------------|
| Type | Type of file to be generated. Types: `CSV`, `Text` and `PDF` |
| File name | Name of the file to be generated |
| Data | Data that will be used to construct the file. Its format will depend on the file type, as specified in the following section |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::tip
Check how to run **[generate file action using RunJS](/docs/how-to/run-actions-from-runjs/#generate-file)**.
:::
### CSV Data Format
To use the `CSV` file format, the data field should contain an array of objects. ToolJet assumes that the keys in each object are the same and represent the column headers of the CSV file.
Example:
```javascript
{{
[
{ name: 'John', email: 'john@tooljet.com' },
{ name: 'Sarah', email: 'sarah@tooljet.com' },
]
}}
```
Using the above code snippet will generate a CSV file with the following content:
```csv
name,email
John,john@tooljet.com
Sarah,sarah@tooljet.com
```
### Text Data Format
To use the `Text` file format, the data field should contain a string.
If you want to generate a text file based on an array of objects, you need to stringify the data before providing it.
For example, if you are using the table component to provide the data, you can enter **`{{JSON.stringify(components.table1.currentPageData)}}`** in the Data field.
### PDF data format
The PDF data format supports two types of input: either a `string` or an `array of objects`. When using an array of objects, the resulting PDF will display the data in a tabular format with columns and rows. On the other hand, if a string is provided, the generated PDF will consist of plain text.

View file

@ -0,0 +1,18 @@
---
id: go-to-app
title: Go to app
---
This action allows you to open any released ToolJet application when an event occurs. Only the apps that are released can be opened using this action.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/gotoapp/gotoapp3.png" alt="ToolJet - Action reference - Open webpage" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: logout
title: Logout
---
This action allows you to log out of the application (ToolJet).
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/logout/logout2.png" alt="ToolJet - Action reference - Logout" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: open-webpage
title: Open webpage
---
You can use this action to open a webpage(on a new tab) for any event.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/open-webpage/open2.png" alt="ToolJet - Action reference - Open webpage" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: run-query
title: Run Query
---
This action allows you to fire queries when an event occurs.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/run-query/run-query2.png" alt="ToolJet - Action reference - Run Query" width="700" />
</div>

View file

@ -0,0 +1,58 @@
---
id: set-localstorage
title: Set localStorage
---
# Set localStorage
This action allows you to specify a `key` and its corresponding `value` to be stored in local storage of the browser. Local storage can be useful in a lot of scenarios. Some of the most common use cases of the local storage includes:
- Saving form values so that users don't accidentally lose them if they reload the page
- Storing any kind of data that is not going to be transferred to the database
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Example: Setting a Component Value Based on Local Storage
1. Add **Text Input**, **Button** and **Text** components to the canvas.
<div style={{textAlign: 'center', marginBotton:'25px'}}>
<img className="screenshot-full" src="/img/actions/localstorage/add-components.png" alt="Add Components To The Canvas" />
</div>
2. Select the Button, add a new event handler, and add a `Set local storage` action with `key` set to `localtest` and `value` set to `{{components.textinput1.value}}`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/localstorage/set-local-storage.png" alt="Set Local Storage" />
</div>
This will set a local storage value with `localtest` as the key and the value entered in the Text Input component as its value.
3. Create a `Run JavaScript code` query, and enter the code below:
```js
return localStorage.getItem("localtest");
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/localstorage/create-runjs-query.png" alt="Create RunJS Query" />
</div>
Click on the **Run** button in the Query Panel. This query will fetch the `localtest` local storage variable that we had set earlier.
4. Select the **Text** component. Under its `Text` property, enter `{{queries.runjs1.data}}`. Now, the Text component will display the value returned by the `Run JavaScript code` query - the local variable we had set earlier.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/localstorage/update-text-component.png" alt="Update Value Of Text Component Based On Local Storage" />
</div>
5. Select the Button component. Add a new event handler to it, add a `Run query` action, select `runjs1` as the query, and set a debounce of `300`.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/actions/localstorage/update-text-on-button-click.png" alt="Updating Text On Button Click" />
</div>
Now, every time you click on the Button component, it will set the local storage value, and the Text component will display the value set in local storage.
:::info
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::
</div>

View file

@ -0,0 +1,36 @@
---
id: set-page-variable
title: Set page variable
---
Page variables are restricted to the page where they are created and cannot be accessed throughout the entire application like regular variables.
Use this action to establish a variable and assign a value to it within the [Multipage Apps](/docs/tutorial/pages).
By default, the debounce field is left empty. However, you can input a numeric value to indicate the time in milliseconds before the action is executed. For example, `300`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/page/setpagevar2.png" alt="ToolJet - Action reference - Switch page" width="600"/>
</div>
## Using RunJS query to set page variable
Alternatively, the set page variable action can be triggered via a RunJS query using the following syntax:
```js
await actions.setPageVariable('<variablekey>',<variablevalue>)
```
`variablekey` must be provided as a string (enclosed in quotes), while the `variablevalue` does not require quotation marks if it is a numerical value.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/page/setpagevar33.png" alt="ToolJet - Action reference - Switch page" />
</div>
:::info
For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
:::

View file

@ -0,0 +1,24 @@
---
id: set-table-page
title: Set Table Page
---
Use this action to change the page index in the table widget.
## Options
| Option | Description |
|--------|-------------|
| Table | Select table from the dropdown |
| Page Index | Numerical value for the page index. ex: `{{2}}` |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/settablepage/page2.png" alt="ToolJet - Action reference - Open webpage" width="700" />
</div>

View file

@ -0,0 +1,24 @@
---
id: set-variable
title: Set variable
---
This action allows you to create a variable and assign a `value` to it.
## Options
| Option | Description |
|--------|-------------|
| Key | Name(String) of the variable through which you can access the value |
| Value | A value can be a string, number, boolean expression, array, or object |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/setvar/setvar2.png" alt="ToolJet - Action reference -Set variable" width="700" />
</div>

View file

@ -0,0 +1,23 @@
---
id: show-alert
title: Show alert
---
This action allows you to display an alert message.
You can set a custom **message** for the alert and choose a particular alert type.
There are 4 types of alert messages - **Info**, **Success**, **Warning**, and **Error**.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/show-alert/alert2.png" alt="ToolJet - Action reference - Show Alert" width="700" />
</div>

View file

@ -0,0 +1,18 @@
---
id: show-modal
title: Show modal
---
Use this action to show the modal for an event.
Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300`
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/showmodal/showmodal2.png" alt="ToolJet - Action reference - Show modal" width="700" />
</div>

View file

@ -0,0 +1,55 @@
---
id: switch-page
title: Switch Page
---
Utilize this action with various event handler to transition to a different page within the [Multipage App](/docs/tutorial/pages).
By default, the debounce field is left empty. However, you can input a numeric value to indicate the time in milliseconds before the action is executed. For example, `300`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/page/switchpage2.png" alt="ToolJet - Action Reference - Page Switching" width="500"/>
</div>
## Query Params
Query parameters can be passed through action such as `Switch Page`. The parameters are appended to the end of the application URL and are preceded by a question mark (`?`).
Query parameters are composed of key-value pairs, where the `key` and `value` are separated by an equals sign (`=`). Multiple query parameters can be included by clicking on the `+` button.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/page/queryparam1.png" alt="ToolJet - Action Reference - Page Switching"/>
</div>
In the above screenshot, we have provided the `username` as the key and the value is `{{globals.currentUser.email}}` which gets the email of the signed in user dynamically. When the button is clicked to trigger the `Switch Page` event handler attached to it then the URL on the switched page will have the parameters.
They are commonly used to provide additional information to the server or to modify the behavior of a web page. They can be used for filtering search results, pagination, sorting, and various other purposes.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/page/queryparam2.png" alt="ToolJet - Action Reference - Page Switching"/>
</div>
## Using RunJS query to switch page
Alternatively, the switch page action can be activated via a RunJS query using the following syntax:
```js
await actions.switchPage('<page-handle>')
```
:::info
For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
:::
### Switch page with query params
The switch page action can also be triggered along with query parameters using the following syntax:
```js
actions.switchPage('<pageHandle>', [['param1', 'value1'], ['param2', 'value2']])
```

View file

@ -0,0 +1,20 @@
---
id: unset-all-page-var
title: Unset All Page Variables
---
Using this action you can unset all the page level variables at once.
## Options
| <div style={{ width:"100px"}}> Option </div> | Description |
|:-------|:------------|
| Run Only if | Add a condition. |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. For example: `300` |
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<img className="screenshot-full img-full" src="/img/actions/unsetAllPageVar/unsetAllPageVar.png" alt="ToolJet - Action reference -Unset variable" />

View file

@ -0,0 +1,20 @@
---
id: unset-all-var
title: Unset All Variables
---
Using this action you can unset all the app level variables at once.
## Options
| <div style={{ width:"100px"}}> Option </div> | Description |
|:-------|:------------|
| Run Only if | Add a condition. |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. For example: `300` |
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<img className="screenshot-full img-full" src="/img/actions/unsetAllVar/unsetAllVar.png" alt="ToolJet - Action reference -Unset variable" />

View file

@ -0,0 +1,27 @@
---
id: unset-page-variable
title: Unset page variable
---
Utilize this action to clear a variable that was established using the [set page variable action](/docs/actions/set-page-variable).
By default, the debounce field is left empty. However, you can input a numeric value to indicate the time in milliseconds before the action is executed. For example, `300`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/page/unsetpagevar2.png" alt="ToolJet - Action reference - Switch page" width="600"/>
</div>
## Using RunJS query to unset variable
Alternatively, the unset page variable action can be triggered via a RunJS query using the following syntax:
```js
await actions.unsetPageVariable('<variablename>')
```
`variablename` is the key of the variable that was provided while creating the variable.
:::info
For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
:::

View file

@ -0,0 +1,23 @@
---
id: unset-variable
title: Unset variable
---
This action allows you to remove the variable that was created using the set variable action.
## Options
| Option | Description |
|--------|-------------|
| Key | Name(String) of the variable through which you can access the value |
| Debounce | Debounce field is empty by default, you can enter a numerical value to specify the time in milliseconds after which the action will be performed. ex: `300` |
:::info
You can also trigger actions from the **JavaScript code**. Check it out [here](/docs/how-to/run-actions-from-runjs).
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/actions/unsetvar/unsetvar2.png" alt="ToolJet - Action reference -Unset variable" width="700" />
</div>

View file

@ -0,0 +1,131 @@
---
id: anti-patterns
title: Anti-Patterns to Avoid
---
When building applications with ToolJet, it's essential to follow best practices to ensure your apps are efficient, maintainable, and provide a smooth user experience. This documentation outlines common anti-patterns to avoid while using ToolJet and offers solutions to optimize your applications.
---
## 1. Unmanaged Component Naming
- **Anti-Pattern**: Using default or non-descriptive names for components.
- **Solution**: **Rename all components with meaningful names to make the apps more manageable as they grow.**
- **Reason**: Descriptive names improve readability, making it easier for you and others to understand and maintain the app's structure.
---
## 2. Exceeding Component Limits
- **Anti-Pattern**: Having more than 2,500 components in a single app.
- **Solution**: **Limit each app to a maximum of 2,500 components.**
- **Reason**: Exceeding this number can slow down the app builder and live apps, impacting both development speed and user experience.
---
## 3. Client-Side Operations for Large Data Sets
- **Anti-Pattern**: Handling large data sets with client-side operations on the Table component.
- **Solution**: **Implement [server-side operations](/docs/widgets/table/serverside-operations/overview) for handling large data sets.**
- **Reason**: Server-side operations reduces the amount of data loaded at once, improving load times and performance.
---
## 4. Simultaneous Execution of Multiple JavaScript Queries
- **Anti-Pattern**: Triggering a large amount of JavaScript queries simultaneously through a single event. For example, using an event to trigger a **Run JavaScript code** query that contains code to execute 15-20 other **Run JavaScript code** queries within the application.
- **Solution**: **Limit the number of simultaneous JavaScript queries triggered by a single event.**
- **Reason**: Triggering numerous Run JavaScript queries at the same time can significantly degrade browser performance as it each JavaScript query creates **[new execution environment](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth#javascript_execution_contexts)** within the browser. JavaScript in browsers runs on a single main thread. When multiple scripts are executed concurrently, they compete for execution time on this thread.
---
## 5. Storing Base64 Data in Variables
- **Anti-Pattern**: Capturing and storing Base64 data directly in variables.
- **Solution**: **Store large data, like base64 images, in a database and retrieve it as needed.**
- **Reason**: Storing Base64 data in variables can consume significant memory and slow down the app. Retrieving data from a database as needed optimizes performance.
---
## 6. Loading All Tabs Simultaneously
- **Anti-Pattern**: Loading all items in the Tab component at once when there are numerous tabs.
- **Solution**: **Enable the “Render only active tabs” option.**
- **Reason**: This prevents unnecessary loading of inactive tabs, reducing initial load times and improving performance.
---
## 7. Excessive Number of Pages in an App
- **Anti-Pattern**: Including too many pages within a single app.
- **Solution**: **Limit the number of pages per app to maintain optimal performance.**
- **Reason**: An excessive number of pages can slow down the app and make it difficult to manage. Organize content efficiently and consider splitting the app if necessary.
---
## 8. Using Non-Blocking Commands in JavaScript for Synchronous Operations
- **Anti-Pattern**: Using non-blocking commands like `Promise.all` and `setTimeout` in the **Run JavaScript code** query when an accurate isLoading state is needed.
- **Solution**: **Avoid non-blocking operations in JavaScript Queries if you require an accurate isLoading status. Ensure your code is synchronous within the Run JavaScript code query.**
- **Reason**: Non-blocking operations can cause **Run JavaScript code** query to exit before these commands complete, leading to an incorrect isLoading status and potentially confusing users.
---
## 9. Triggering Unnecessary Queries on Page Load
- **Anti-Pattern**: Triggering all queries on page load, regardless of their necessity.
- **Solution**: **For multi-page apps, only trigger queries on page load that are needed for the specific page.**
- **Reason**: Loading unnecessary data consumes resources and slows down page load times. Optimizing queries enhances performance.
---
## 10. Using Actions inside Loop Functions
- **Anti-Pattern**: Using actions inside loop functions.
Example:
You have a Table displaying data from `{{page.variables.data}}` and a **Save Changes** button that updates the data. When users edit rows and click **Save Changes**, you might initially implement the update like this:
```javascript
const data = page.variables.data;
Object.values(components.table1.dataUpdates).forEach(ele => {
data[ele.id] = ele;
actions.setPageVariable("data", data);
});
```
The setPageVariable action is executed inside the loop for each row update. This causes the table to re-render every time the variable is updated, leading to significant performance degradation, especially when multiple rows or cells are updated simultaneously.
- **Solution**: **Modify your code to update the page variable once after all changes are processed**:
```javascript
const data = page.variables.data;
Object.values(components.table1.dataUpdates).forEach(ele => {
data[ele.id] = ele;
});
actions.setPageVariable("data", data);
```
- **Reason**: By updating the variable after the loop completes, the table re-renders only once. This reduces unnecessary processing and significantly improves performance when handling multiple updates.
---
## 11. Direct Mutation of Data
- **Anti-Pattern**: Directly mutating data structures through JavaScript code, such as using `queries.getEmployees.data = []`.
- **Solution**: Always use ToolJet's built in **[actions](/docs/how-to/run-actions-from-runjs/)** to manipulate data.
- **Reason**: Direct mutation of data can lead to unexpected bugs and make debugging more complex.
---
## 12. Naming Component/Query with Hyphen or Space
- **Anti-Pattern**: Naming components or queries with hyphens or spaces in between, such as `run-py1` or `my query`.
- **Solution**: **Use names without hyphens or spaces**, or reference them using bracket notation (e.g., `{{queries['run-py1'].isLoading}}`).
- **Reason**: Hyphens and spaces can cause syntax issues. Using bracket notation or avoiding these characters ensures consistency and prevents errors in query or component references.
---
## Conclusion
Avoiding these anti-patterns when using ToolJet ensures that your applications are efficient, responsive, and maintainable. By following these best practices, you can enhance user experience and simplify app management. Always consider the impact of your development choices on both performance and scalability.

View file

@ -0,0 +1,69 @@
---
id: canvas
title: Canvas and Layout
---
**Canvas** is the main area in the app-builder where you build your application and design the user interface.
<img className="screenshot-full img-full" src="/img/app-builder/canvas/canvas.png" alt="App Builder: Canvas"/>
## Customizing Canvas
Through Global Settings, you can customize the following properties of the **Canvas**:
- **Max width of canvas**: Defines the maximum width of the canvas, which can be set in pixels or as a percentage of the screen size. The height of the canvas expands automatically as more components are added.
- **Canvas background**: Sets the background color of the canvas. This can also be controlled dynamically by clicking on **fx** and entering a logical expression.
- **App mode**: You can choose from three theme modes
- **Auto**: Adapts to the browser's theme settings or allows users to switch between light and dark modes.
- **Light**: Keeps the app in light mode, users cannot switch to dark mode.
- **Dark**: Keeps the app in dark mode, users cannot switch to light mode.
<img className="screenshot-full img-s" src="/img/app-builder/canvas/global-settings.png" alt="App Builder: Canvas"/>
## Building the User Interface
To build the user interface, components can be dragged from the [Components Library](/docs/beta/app-builder/building-ui/component-library) on the right. Use the Component Handle to reposition a component. A component can be resized from any of its edges or corners.
<img className="screenshot-full img-full" src="/img/app-builder/canvas/drag.gif" alt="App Builder: Canvas"/>
### Grid, Snapping and Markers
ToolJet's Canvas provides a grid background, smart snapping, and visual markers to support precise alignment and positioning of components. Components automatically snap to grid lines and nearby elements, reducing the need for manual adjustments. Each cell on the canvas grid has a fixed height of 10 pixels. The width of each cell adjusts based on the screen size.
<img className="screenshot-full img-m" src="/img/app-builder/canvas/snap.png" alt="App Builder: Canvas"/>
## Creating Layout
In ToolJet, components can be grouped using a Layout component such as a **[Container](/docs/widgets/container)** or a **[Form](/docs/widgets/form)**. You can drag and drop the relevant components into the layout components on the canvas to create a section.
<img className="screenshot-full img-m" src="/img/app-builder/canvas/form.png" alt="App Builder: Canvas"/>
## Managing Components on Canvas
#### Select and Move Multiple Components
You can select multiple components by clicking and dragging over them, or by clicking individually while holding the Shift key. Once selected, all components can be moved together as a group.
#### Copy Components
Components on the canvas can be copied using **Cmd/Ctrl + C**.
<img className="screenshot-full img-m" src="/img/app-builder/canvas/copy.png" alt="App Builder: Canvas"/>
#### Paste Components
Copied components can be pasted onto the canvas using **Cmd/Ctrl + V**.
<img className="screenshot-full img-m" src="/img/app-builder/canvas/paste.png" alt="App Builder: Canvas"/>
#### Clone Components
Components on the canvas can be cloned using **Cmd/Ctrl + D**. Unlike copy and paste, cloning creates a duplicate of the selected component instantly.
<img className="screenshot-full img-m" src="/img/app-builder/canvas/clone.png" alt="App Builder: Canvas"/>
<br/><br/>
:::note
For additional shortcuts, refer to the [Keyboard Shortcuts Guide](/docs/tutorial/keyboard-shortcuts).
:::

View file

@ -0,0 +1,28 @@
---
id: component-library
title: Component Library
---
**Components** are the building blocks of ToolJet applications, used to create interactive user interfaces. The **Component Library**, located on the right, contains all [available components](#available-components).
Components can be added to the canvas by dragging and dropping them onto the canvas at your desired location.
<img className="screenshot-full img-full" src="/img/app-builder/components/component-library.png" alt="App Builder: Component library"/>
## Available Components
| Category | Components |
|:----------|:------------|
| **Buttons** | [Button](/docs/widgets/button), [Button Group](/docs/widgets/button-group) |
| **Data** | [Table](/docs/widgets/table/), [Chart](/docs/widgets/chart/)|
| **Layouts** | [Form](/docs/widgets/form), [Modal](/docs/widgets/modal), [Container](/docs/widgets/container), [Tabs](/docs/widgets/tabs), [ListView](/docs/widgets/listview), [Kanban](/docs/widgets/kanban), [Calendar](/docs/widgets/calendar) |
| **Text Inputs** | [Text Input](/docs/widgets/text-input), [Text Area](/docs/widgets/textarea), [Email Input](/docs/widgets/email-input), [Password Input](/docs/widgets/password-input), [Rich Text Editor](/docs/widgets/rich-text-editor) |
| **Number Inputs** | [Number Input](/docs/widgets/number-input), [Phone Input](/docs/widgets/phone-input), [Currency Input](/docs/widgets/currency-input), [Range Slider](/docs/widgets/range-slider), [Star Rating](/docs/widgets/star-rating) |
| **Select Inputs** | [Dropdown](/docs/widgets/dropdown), [Multi-Select](/docs/widgets/multiselect), [Toggle Switch](/docs/widgets/toggle-switch-v2), [Radio Button](/docs/widgets/radio-button), [Checkbox](/docs/widgets/checkbox), [Tree Select](/docs/widgets/tree-select) |
| **Date and Time Inputs** | [Date-range Picker](/docs/widgets/date-range-picker), [Date Picker](/docs/widgets/date-picker-v2), [Time Picker](/docs/widgets/time-picker), [Date-Time Picker](/docs/widgets/datetime-picker-v2) |
| **Navigation** | [Link](/docs/widgets/link), [Pagination](/docs/widgets/pagination), [Steps](/docs/widgets/steps) |
| **Media** | [Icon](/docs/widgets/icon), [Image](/docs/widgets/image), [SVG Image](/docs/widgets/svg-image), [PDF](/docs/widgets/pdf), [Map](/docs/widgets/map) |
| **Presentation** | [Text](/docs/widgets/text), [Tags](/docs/widgets/tags), [Circular Progressbar](/docs/widgets/circular-progress-bar), [Timeline](/docs/widgets/timeline), [Divider](/docs/widgets/divider), [Vertical Divider](/docs/widgets/vertical-divider), [Spinner](/docs/widgets/spinner), [Statistics](/docs/widgets/statistics), [Timer](/docs/widgets/timer) |
| **Custom** | [Custom Component](/docs/widgets/custom-component), [HTML Viewer](/docs/widgets/html), [iFrame](/docs/widgets/iframe) |
| **Miscellaneous** | [Filepicker](/docs/widgets/file-picker), [Code Editor](/docs/widgets/code-editor), [Color Picker](/docs/widgets/color-picker), [Bounded Box](/docs/widgets/bounded-box), [QR Scanner](/docs/widgets/qr-scanner) |
| **Legacy** | [Modal](/docs/widgets/modal), [Datetime Picker](/docs/widgets/datepicker/), [Radio Button](/docs/widgets/radio-button), [Toggle Switch](/docs/widgets/toggle-switch), [Dropdown](/docs/2.50.0-LTS/widgets/dropdown), [Multiselect](/docs/widgets/multiselect) |

View file

@ -0,0 +1,43 @@
---
id: component-properties
title: Component Properties
---
**Component properties** define the appearance, behavior, and interactivity of UI components in ToolJet. It also allows you to configure component-level permissions, enabling only selected users or user groups to interact with the component.
Each component includes a unique set of properties based on its functionality. Heres an overview of common types of configurable properties:
- **Labels and Data Fields**: For input components, you can configure the label, add placeholders, default values, define validation rules, etc.
- **Data**: Populate components with static values or dynamic data through queries.
- **Events**: Events are actions or triggers that respond to user interactions or specific conditions in your application. They let you define custom logic (like running a query, navigating to a page, or showing a toast) in response to user activity or application changes — without writing backend code.
- **Styles**: Define visual attributes like colors, spacing, alignment, and border radius to adjust how the component appears.
- **State**: Control component states such as loading, visibility, or whether the component is disabled. You can toggle these manually or control them using logical expressions.
- **Device**: Configure whether the component should be visible on specific devices, such as mobile or desktop.
<img className="screenshot-full img-full" src="/img/app-builder/components/properties-panel.png" alt="App Builder: Component library"/>
These are just a few commonly used property types. For detailed information on any specific component and its properties, refer to their individual documentation.
## Component Level Permissions
You can configure component-level permissions to allow only selected end users or user groups to interact with the component. The component will not render at all for users who dont have access.
Suppose you're building an app to manage customer license details. Sales representatives should be able to create, update, and delete customer information. Meanwhile, Product, Marketing, and Customer Success teams should only view this data. To enforce this, you can configure component-level permissions to hide the Edit and Delete buttons from non-sales users. These buttons wont render at all for users without access.
### Configuring Component Level Permission
Follow these steps to configure component level permission:
**Role Required**: Admin or Builder
1. Select the component, then click the kebab menu (three dots) next to the component name in the properties panel.
<img className="screenshot-full img-l" style={{ marginTop: '15px' }} src="/img/app-builder/components/permission-kebab.png" alt="App Builder: Component library"/>
2. Select **Component permission**. <br/>
<img className="screenshot-full img-s" style={{ marginTop: '15px' }} src="/img/app-builder/components/component-permission.png" alt="App Builder: Component library"/>
3. Select the **Type**:
- **All users with access to the app**: Grants access to all users who can access the application.
- **Users**: Select specific users from the dropdown. Note: These users must already have access to the application.
- **User groups**: Restricts access to members of selected user groups. Note: The selected user groups must have access to the application.
<img className="screenshot-full img-s" style={{ marginTop: '15px' }} src="/img/app-builder/components/permission-type.png" alt="App Builder: Component library"/>
**Note**: If a component's permissions have been configured by an admin and the builder is not included in the allowed users or groups, the builder will not be able to modify the components permissions.

View file

@ -0,0 +1,36 @@
---
id: component-state
title: Component State
---
Every ToolJet component maintains a state—a set of values representing its current data and configuration. This state can be accessed through exposed variables, which allow components to interact with other parts of the application. For example, the value entered into a text input component can be passed to a query to fetch data from the database.
Each component has a unique set of exposed variables based on its functionality — for example, a **Table** component exposes `selectedRow`, a checkbox exposes `isChecked`, and so on.
Component states in ToolJet are dynamic and can be modified at runtime using built-in functions called [Component-Specific Actions (CSAs)](/docs/beta/app-builder/events/use-case/csa), such as `reset()`, `setValue()`, and `setVisibility()`. These actions let you trigger logic in response to user interactions.
Component states can be used across the application to build interactive and reactive experiences:
- In queries — to send user inputs or component values as parameters.
- In other components — to conditionally display, update, or interact with components.
## Available Component States
In the App-Builder, you can view all available component states using the Inspector located in the left sidebar. For more details, refer to the [Inspector](/docs/beta/app-builder/debugging/inspector) guide.
<img style={{ marginBottom:'15px' }} className="screenshot-full img-s" src="/img/app-builder/access-comp-data/inspector.png" alt="App Builder: Properties Panel"/>
- Open the Components dropdown inside the Inspector.
- Select the component whose state you want to inspect.
- A secondary dropdown will appear showing all the available states.
You can also copy the state value or its path, which can be used to access it from another component or query. When you hover over a state in the Inspector, two icons appear — one for copying the path and one for copying the value.
<img className="screenshot-full img-s" style={{ marginBottom:'15px' }} src="/img/app-builder/access-comp-data/comp-inspect.png" alt="App Builder: Properties Panel"/>
## Accessing Component State
You can access a components state using the following syntax: <br/>
`{{components.<component-name>.<variable-name>}}`
Example: `{{components.numberinput1.value}}` - This will fetch the value entered by the user in the **numberinput1** component.

View file

@ -0,0 +1,136 @@
---
id: pages
title: Pages and Navigation
---
ToolJet allows the creation of multi-page applications, helping you break your application into different sections. Instead of building everything on a single screen, you can create separate pages to organize different functionalities or enable navigation within your application.
You can add the following items to the navigation menu in ToolJet:
1. **New Page**: Create new pages to support multi-page applications and organize functionalities more effectively.
2. **Web Pages**: Add external URLs to the navigation menu to redirect users to specific webpages.
3. **ToolJet Application**: Link to other ToolJet applications directly from the nav menu. Note: The application must be a released app within the same workspace.
4. **Nav Group**: Group related navigation items together to simplify navigation in complex applications. For example, all admin-related items can go under one group, and user-related items under another.
This guide discusses how pages and navigation menu work, how to create new navigation items manage them.
## Page Properties
Each page in ToolJet comes with properties that define its identity and behavior. These properties help in organizing, referencing, and securing pages within your application.
<img className="screenshot-full img-m" src="/img/app-builder/multi-page/dropdown.png" alt="App Builder: Canvas"/>
### Name
A display name for the page, shown in the application's navigation menu. It is also used to reference the page within the ToolJet application. You can optionally add an icon to make the page easier to identify in the menu. The page name and icon can be updated using the kebab menu (three dots) next to the page name and then going to edit page details.
<img className="screenshot-full img-m" src="/img/app-builder/multi-page/page-name.png" alt="App Builder: Canvas"/>
### Handle
The page handle is a unique identifier used to generate a shareable URL for the page. It is appended as a slug to the end of your application URL. By default, it's auto-generated from the page name. You can change it manually from edit page details option.
### Home Page
The home page is the default landing page when the app launches. Only one page can be designated as the home page in your application. It cannot be deleted, disabled, or hidden from the page menu. A page can be marked as the home page using the kebab menu (three dots) next to the page name or by going to edit page details option.
### Permissions
Page permissions control who can access a particular page. You can choose to:
- Allow access to all users with application access
- Restrict access to selected users
- Restrict access to selected [user groups](/docs/user-management/role-based-access/user-roles)
To configure page permissions, click the kebab menu (three dots) next to the page name, select Page permission, and select a permission option from the popup.
### Disable Page
**Disable Page** allows you to disable a page, making it inaccessible in the released application. A page marked as Home cannot be disabled.
### Hide Page on Navigation Menu
You can hide a page from the navigation menu in the released application. However, hidden pages can still be accessed using the Switch Page action or by navigating directly to the page URL. Pages set as Home cannot be hidden.
## Header and Navigation Menu
### App Header
The app header section allows you to control what is displayed in the applications header.
<img className="screenshot-full img-m" src="/img/app-builder/multi-page/app-header.png" alt="App Builder: Canvas"/>
- **Show app header**: Toggle this on to display the app header.
- **Show logo**: Toggle this on to display the application logo. You can update the logo from the [white labeling](/docs/tj-setup/org-branding/white-labeling/) settings.
- **Title**: Set a title for the application. This will be displayed in the app header.
### Navigation Menu
The **Navigation Menu** lets users navigate between pages, external web pages and other ToolJet applications in your application. You can customize how it looks and works, or even hide certain pages from it.
#### Show Navigation Menu
Toggle this on to display the navigation menu. When disabled, no navigation menu will be displayed, but users will be still able to navigate using events and page urls.
#### Position
Choose whether to display the navigation menu at the top or on the side of the application.
**Top Navigation Menu** <br/>
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/multi-page/top-nav.png" alt="App Builder: Canvas"/>
**Side Navigation Menu** <br/>
<img className="screenshot-full img-full" src="/img/app-builder/multi-page/side-nav.png" alt="App Builder: Canvas"/>
#### Style
**Top Navigation Menu** <br/>
Choose the display style for the top navigation menu: Text only or Text + Icon. The top navigation menu cannot be collapsed.
**Side Navigation Menu** <br/>
Choose display styles from: Text only, Icon only, or Text + Icon. The side navigation menu also supports a collapsible layout.
## Event Handlers
The Page Event Handler lets you trigger actions automatically when a page loads. Use it to prepare data, set default values, or run any required logic.
For example, it can run a query to fetch the latest data from the database and populate it in a table component. This ensures the page is ready with up-to-date information whenever it is loaded.
<img className="screenshot-full img-full" src="/img/app-builder/multi-page/page-event.png" alt="App Builder: Canvas"/>
## Exposed Variables
Exposed variables are values from a page that can be accessed throughout the application. These include default page-level values like page name, page ID, and page handle. In addition to the default ones, custom page variables can also be defined and accessed as exposed variables.
| Variable | Description | How To Access |
| ----------- | ----------- | ------------- |
| handle | Represents the slug of the page within the app. It is automatically set when a page is created, but can be [renamed](#handle) from the page options. | `{{page.handle}}`|
| name | Indicates the name of the page. | `{{page.name}}` |
| id | Each page in the ToolJet application receives a unique identifier upon creation. | `{{page.id}}` |
| variables | Variables object contains all the variables created for a specific page using the [Set Page Variable](/docs/actions/set-page-variable) action. | `{{page.variables.<pageVariableName>}}`, where `<pageVariableName>` refers to the variable name. |
## Manage Navigation Item
### New Page
You can add a new page to organize the application navigation or to separate different parts of your app. To add a new page, click on the **+ New page** button at the bottom of the Pages and menu panel. Enter the page name and press enter to create the page.
<img className="screenshot-full img-s" src="/img/app-builder/multi-page/new-page.png" alt="App Builder: Canvas"/>
### Web Page
To link an external web page to the navigation menu, click the kebab menu (three dots) next to the **+ New Page** button, then select **Add nav item with URL**.
Enter a name and provide the URL. You can also choose whether to open the web page in a new tab or in the same tab, and optionally select an icon for the navigation item.
<img className="screenshot-full img-s" src="/img/app-builder/multi-page/webpage.png" alt="App Builder: Canvas"/>
### ToolJet App
To add a ToolJet application to the navigation menu, click the kebab menu (three dots) next to the **+ New Page** button, then select **Add nav item ToolJet app**.
Enter a name and select the application from the dropdown. Only the release application from the same workspace will appear in the dropdown. You can also choose whether to open the application in a new tab or in the same tab, and optionally select an icon for the navigation item.
<img className="screenshot-full img-s" src="/img/app-builder/multi-page/tooljet-app.png" alt="App Builder: Canvas"/>
### Nav Group
Related navigation item can be grouped together using the nav group. To add a new nav group, click the kebab menu (three dots) next to the **+ New Page** button, then select **Add nav group**. Enter the group name and press enter to create the group. You can then drag items into the group folder. You can also add an icon to the group for better visual identification.
<img className="screenshot-full img-s" src="/img/app-builder/multi-page/new-group.png" alt="App Builder: Canvas"/>

View file

@ -0,0 +1,24 @@
---
id: accessing-query-results
title: Accessing Query Results
---
Once your query is created and executed, the next step is to actually use the data, whether youre displaying it in a table, populating dropdowns, or using it in logic for another query. Lets walk through how you can work with query results.
To better understand what your query is returning, use the Inspector panel. Click on the Inspect button, select your query from the query dropdown. You'll find the following keys:
- **data**: The processed response returned from the query. This is the data you typically bind to components.
- **rawData**: The original API response. It's useful for debugging.
- **isLoading**: A boolean indicating whether the query is currently running. Great for showing loaders or disabling buttons during fetches.
<img className="screenshot-full img-s" src="/img/app-builder/accessing-query-data/inspector.png" alt="App Builder: Query Panel"/>
<br/>
You can pass query results to a component by using the `{{ }}` syntax. For example, if you have a query named *getEmployees*, you can pass its data to a Table component by setting the table's data property to `{{queries.getEmployees.data}}`. Learn more about binding queries to components [here](/docs/app-builder/connecting-with-data-sources/binding-data-to-components).
### Quick Actions
In the Inspector panel, when you hover over a property like data, youll see two icons. These icons allow you to quickly copy either the path or value of that property. Heres what they do:
1. Copy Path - Copies the full path (e.g., `{{queries.getEmployees.data}}`) so you can reference it directly into component fields.
2. Copy Value - Copies the actual data returned, useful when inspecting values or mocking responses.
These icons are available for every property, making it easier to wire up your data to components.

View file

@ -0,0 +1,52 @@
---
id: binding-data-to-components
title: Binding Data to Components
---
In this section, youll learn how to connect and bind data to components within ToolJet, whether the data comes from a data source or other components in your app.
You can display data from your data source queries in the components using the `{{ }}` syntax. For instance, you can pass data to a **Table** component's Data property using the following format: `{{ queries.<query-name>.data }}`
For example, you are working on an Employee Directory app where you want to show all employees in a table. If you have a query named *listEmployees* that returns an array of employee objects, you can pass its data to a **Table** component by setting the table's data property to `{{queries.listEmployees.data}}`.
<img className="screenshot-full img-full" style={{marginBottom:'15px'}} src="/img/app-builder/connecting-with-datasouces/binding.png" alt="App Builder: bininding data to components"/>
ToolJet also supports JavaScript expressions inside `{{ }}`, allowing you to dynamically transform data before display. Here are a few use cases:
## Use cases
### Filtering Data
If you want to show only employees from the Engineering department:
```js
{{ queries.listEmployees.data
.filter(employee => employee.department === 'Engineering') }}
```
### Map Data
If you want to show only employee names in a dropdown:
```js
{{ queries.listEmployees.data
.map(employee => employee.name) }}
```
### Conditional Rendering
If you want to show a greeting if an employee is selected in a table:
```js
{{ components.table1.selectedRow ? `Hello, ${components.table1.selectedRow.name}` : "" }}
```
### Chaining Expressions
You can also chain multiple JavaScript methods for more complex transformations. For example, filtering and then mapping:
```js
{{ queries.listEmployees.data
.filter(emp => emp.department === 'Engineering')
.map(emp => emp.name.toUpperCase()) }}
```
These expressions give you control over how data is displayed and interacted with inside your ToolJet applications.

View file

@ -0,0 +1,110 @@
---
id: creating-managing-queries
title: Creating and Managing Queries
---
A query is a way to interact with your **[data sources](/docs/data-sources/overview)** and acts as the link between your apps UI and your data. Queries connect your app to configured data sources such as SQL, NoSQL, vector databases, APIs, spreadsheets, and cloud services. Whether its retrieving records from your MongoDB collection or updating data in a SQL database, you can use queries to interact with them.
Queries are created in the Query Panel, located at the bottom of the App Builder, where you can either use a visual form-based builder or write code/SQL manually.
<img className="screenshot-full img-full" src="/img/app-builder/connecting-with-datasouces/query-panel.png" alt="App Builder: Query Panel"/>
## Creating a New Query
- Click on the **+** button in the Query Panel to open a menu listing the available data sources or you can add a new data source by clicking on **+ Add new Data Source** button.
- Select the desired data source.
<img className="screenshot-full img-s" src="/img/app-builder/connecting-with-datasouces/create-query.png" alt="App Builder: Create queries"/>
## Configuring the Query
The interface for configuring queries depends on the type of data source. If you are using any SQL data source, you can configure your query using either GUI mode or SQL mode. Rest of the data sources can be configured using form-based GUI.
### GUI mode
- For the Postgres data source, when using GUI mode (as shown in the image below), youll need to select the operations you want to perform and then fill out the required fields.
<img className="screenshot-full img-full" src="/img/app-builder/connecting-with-datasouces/gui-mode.png" alt="App Builder: configure PostgreSQL queries"/>
- In this example using the AWS S3 data source, you can perform the **Upload object** operation to upload a file to an S3 bucket. Youll need to provide details such as the bucket name, key, and other relevant parameters based on the selected operation.
<img className="screenshot-full img-full" src="/img/app-builder/connecting-with-datasouces/aws-gui.png" alt="App Builder: configure AWS S3 queries"/>
### SQL mode
For data sources such as MYSQL, PostgreSQL or SQL Server, you can choose SQL mode where you can write the SQL query to perform your desired operation.
<img className="screenshot-full img-full" src="/img/app-builder/connecting-with-datasouces/sql-mode.png" alt="App Builder: configure PostgreSQL queries"/>
<!-- ## JavaScript and Python queries
You can also create JavaScript or Python queries to manipulate your data from other queries, write business logic, or integrate third-party libraries. Learn more about them in **[Running JavaScript](/docs/data-sources/run-js)** and **[Running Python](/docs/data-sources/run-py)** documentation. -->
## Custom Parameters
You often need a query to fetch different data based on user input, component state, or other logic. Custom parameters allow you to pass dynamic values into a query, making it reusable without hard-coding values.
Let's say you have a query that fetches employee details based on department. Instead of creating a separate query for each department, you can define a parameter like `departmentName`, and use it to filter results dynamically.
To add parameters, simply click the **+ Add** button next to the Parameters label in the query editor.
For each parameter, you need to specify:
- **Name**: The identifier for the parameter.
- **Default value**: This value can be a constant string, number, or object.
**Syntax for utilizing the parameter:** Employ `parameters.<identifier>` in your query. It's important to note that parameters can only be utilized within the specific query where they are defined.
Learn more about **[Using Custom Parameters](/docs/how-to/use-custom-parameters)**.
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui3/queryparams-v2.png" alt="Custom Parameters" style={{marginBottom:'15px'}}/>
## Preview and Run
Before connecting a query to your apps UI, use the Preview button to check what it returns. This is especially useful when working with external APIs or complex SQL. You can inspect the raw or JSON response, debug any issues, and make sure the data matches what your components need.
Once things look good, use the Run button to execute the query and verify how it interacts with your components and other queries.
## Query Level Permission
You can configure query-level permissions to allow only selected end users or user groups to run the query.
### Configuring Query Level Permission
Follow these steps to configure query level permission:
**Role Required**: Admin or Builder
1. Select the query, then click the kebab menu (three dots) next to the query name on the query panel. <br/>
<img className="screenshot-full img-l" style={{ marginTop: '15px' }} src="/img/app-builder/connecting-with-datasouces/query-permission-kebab.png" alt="App Builder: Create queries"/>
2. Select **Query permission**. <br/>
<img className="screenshot-full img-s" style={{ marginTop: '15px' }} src="/img/app-builder/connecting-with-datasouces/query-permission.png" alt="App Builder: Component library"/>
3. Select the **Type**:
- **All users with access to the app**: Grants access to all users who can access the application.
- **Users**: Select specific users from the dropdown. Note: These users must already have access to the application.
- **User groups**: Restricts access to members of selected user groups. Note: The selected user groups must have access to the application.
<img className="screenshot-full img-s" style={{ marginTop: '15px' }} src="/img/app-builder/connecting-with-datasouces/permission-type.png" alt="App Builder: Component library"/>
**Note**: If a query's permissions have been configured by an admin and the builder is not included in the allowed users or groups, the builder will not be able to run or modify the query or its permissions.
## Triggers
Triggers allow you to control when and how a query executes within your application. You can find them under the **Settings** tab in the query editor. Following are the triggers available:
### Run This Query on Application Load
You can use this when you want data to be available as soon as the page loads, like auto-fetching a users dashboard data or populating dropdown options without requiring user input.
### Request Confirmation Before Running Query
For actions that modify or delete data, enable this to prompt users for confirmation. It acts as a safeguard against accidental clicks that could alter critical records.
<img className="screenshot-full img-l" style={{ marginBottom:'15px'}} src="/img/app-builder/connecting-with-datasouces/confirm.png" alt="App Builder: confirmation dialog"/>
### Show Notification on Success
Let users know when actions are completed successfully. This improves UX by giving real-time feedback. You can customize the message and how long it stays visible.
<img className="screenshot-full img-l" style={{ marginBottom:'15px'}} src="/img/app-builder/connecting-with-datasouces/notification.png" alt="App Builder: notification on query run"/>
### Retry on Network Errors
This setting is only available for REST API queries. Here, you get an option to automatically retry REST API requests in case of certain network errors or specific HTTP status codes. By default, it retries a failed API request up to 3 times before marking it as failed. Refer to [REST API Documentation](/docs/data-sources/restapi/querying-rest-api/#retry-on-network-errors) for more details.

View file

@ -0,0 +1,64 @@
---
id: transforming-data
title: Transforming Data
---
Transformations help you clean up your data before passing it to UI components. While building applications, the raw data you fetch from an API or database often needs customization before displaying it in the components. You might need to:
- Convert raw ISO timestamps into DD/MM/YYYY.
- Flatten deeply nested objects for use in tables or dropdowns.
- Convert currency, distance, or temperature values before display.
- Adjust field names to match component expectations.
Thats where data transformations come in. It helps you to shape your backend data into a frontend-friendly format, keeping your UI logic simple and your app easier to maintain.
You can write transformation code in JavaScript or Python. Lets say youre building an employee management dashboard, and your getEmployees API returns a lot of extra data:
```json
[
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"phone_number": "+91876543210",
"department_id": 1,
"salary": "$50k"
},
{ ... }
]
```
But in your table, you only want to display *id*, *name*, *designation* fields. Instead of changing the API or manually filtering inside every component, you can transform the data once, at the query level.
## Using JavaScript
Head to the Transformations tab of your query and write your javascript code:
```javascript
return data.map(item => ({
id: item.id,
name: item.name,
designation: item.designation
}));
```
This ensures every time the query runs, your components receive exactly the shape of data they need.
<img className="screenshot-full img-full" style={{ marginBottom:'15px'}} src="/img/app-builder/connecting-with-datasouces/transformation_js.png" alt="App Builder: query transformations"/>
## Using Python
If youre more comfortable with Python, just switch the language in the transformation tab and use a similar approach:
```python
[
{"id":item['id'],
"name": item['name'],
"designation": item['designation']
} for item in data
]
```
<img className="screenshot-full img-full" style={{ marginBottom:'15px'}} src="/img/app-builder/connecting-with-datasouces/transformation_python.png" alt="App Builder: query transformations"/>
Transformations provide you with an easy way to adjust your data before using it in your applications.

View file

@ -0,0 +1,58 @@
---
id: constants-secrets
title: Referencing Constants and Secrets
---
When building applications in ToolJet, you often need to reuse fixed values (such as URLs or environment flags) or securely handle sensitive information (such as API keys or database credentials). [Workspace Constants and Secrets](/docs/security/constants/) make this process easy, safe, and maintainable, especially when working across multiple apps or with larger teams.
In this guide, you'll learn how to use Workspace Constants and Secrets within your ToolJet apps.
You can create a global constant or secret directly from the ToolJet Dashboard. Once created, these constants and secrets can be referenced by builders within the app-builder.
<img className="screenshot-full img-full" style={{marginBottom:"15px"}} src="/img/security/constants/constants-secret/env-specific-const-v2.png" alt="CMS Page"/>
## Characteristics and Usage
Both constants and secrets allow you to store reusable values for your apps. However, they serve different purposes and have distinct characteristics as shown below:
| Characteristic | Global Constants | Secrets |
|-------------------------|:-----------------------------:|:-------------------------:|
| Components | ✅ | ❌ |
| Data Queries | ✅ | ✅ |
| Encrypted in DB | ✅ | ✅ |
| Masked in Frontend | ❌ | ✅ |
| Resolved on Client Side | ✅ | ❌ |
| Resolved on Server Side | ❌ | ✅ |
| Naming Convention | `{{constants.constant_name}}` | `{{secrets.secret_name}}` |
## Access Control
To maintain security and governance:
- Only Admins can create, edit, or delete Constants and Secrets.
- Builders can reference them in their applications but cannot modify them.
## Use Cases
### Reusable Values Across Apps with Global Constants
Imagine youre building an app that fetches product prices from an API. The base URL of your API is the same across multiple queries.
Instead of hard-coding this URL everywhere, define a Global Constant. Now, if the base URL ever changes, you only need to update it in one place, reducing errors and improving maintainability.
- Name: `API_BASE_URL`
- Value: `https://api.example.com/v1`
You can now reference it in your queries or custom code:
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/constants_usecase.png" alt="constant usecase"/>
### Handling Sensitive Credentials with Secrets
Lets say your application uses a third-party service such as OpenAI that requires an API key. Storing this key directly in queries or code isnt a good practice. Instead, define a Secret:
- Name: `OPENAI_API_KEY`
- Value: `sk_****************`
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/secret_usecase.png" alt="secret usecase"/>
Secrets are encrypted and can only be accessed within queries and data sources. They are not accessible in components, ensuring your credentials remain secure.

View file

@ -0,0 +1,60 @@
---
id: control-components
title: Controlling Components Using Code
---
Apart from [**events**](/docs/docs/app-builder/events/use-case/csa), Component-Specific Actions (CSAs) can also be triggered using code to modify component properties and state.
Lets say you want to:
- Reset a **Form** automatically after submission
- Show or hide components based on a condition
- Update a **Text Input** based on another fields value
- Disable a button during API calls
- Change the active tab programmatically
In each of these cases, you can use CSAs with JavaScript or Python queries.
## How It Works
Each component in ToolJet comes with a set of CSAs. Below are some examples of CSAs:
- setValue() Sets or updates a components value
- clear() Clears the value of an input
- setLoading() Sets or unsets the loading state
- setDisable() Enables or disables a component
- setVisibility() Dynamically controls component visibility
You can trigger these actions from your JavaScript or Python queries.
For example, if you have a **Button** that triggers a query and want to show a loader until the data is loaded. You could use `setLoading()` to show a spinner:
```js
await components.button1.setLoading(queries.getData.isLoading)
```
## Use Cases
### Pre-fill a Form Field Based on User Selection
When a user selects a product from a **Dropdown**, automatically set the price in a **Text Input** component:
```js
await components.textInput1.setValue(components.dropdown1.value)
```
### Clear Fields After Submitting a Form:
After a user submits a **Form**, reset all inputs:
```js
await components.formInput.resetForm()
```
### Close the Modal after Form Submission:
If you are using a **Modal** for collecting data, close it once the **Form** has been submitted successfully:
```js
await components.modal1.close()
```
Using CSAs in your code lets you dynamically control component behavior based on custom logic.

View file

@ -0,0 +1,57 @@
---
title: Using fx for Dynamic Behaviour
id: fx-dynamic-behaviour
---
In ToolJet, you can make your applications more interactive by writing logic directly in component properties using the **fx** editor. For instance, you might want to disable a **Button** until all required form fields are filled, or change the color of an input field based on whether the entered value is valid. You can define this conditional logic using JavaScript expressions in the **fx** editor.
This makes it easy to build intuitive interfaces, with components that respond in real time to user actions and data updates.
## How fx Works
Whenever you see the **fx** icon next to a property in the component settings, it means you can switch to expression mode. Clicking the icon opens an input box where you can write custom logic using JavaScript inside `{{ }}`. You can reference query results, component states, and app-level variables directly within these expressions. ToolJet supports full JavaScript syntax here, including conditional logic, string interpolation, array methods like map, filter, and reduce, and more.
Lets say youre building a form that takes user input. You want the **Submit button** to be enabled only if all form validations pass.
With ToolJets **fx** support, you can achieve this in the Disabled property of the button component like so:
<img className="screenshot-full img-m" src="/img/app-builder/custom-code/button-disable.png" alt=" button disable "/>
This expression disables the **Button** when the **Form** is invalid. No manual toggling needed. Similarly, you could use the same approach to update other properties such as visibility, background color, font size etc. for different components.
If you are new to ToolJet and want to learn how to access component properties, check out [this guide](/docs/app-builder/building-ui/component-state#available-component-states).
## Use Cases
### Loading States
Display loading indicators until the data is loaded.
Example: In an app where you are loading data in the table component, you might want to show a loading spinner while fetching employee data.
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/loading.png" alt=" button disable "/>
### Conditional Styling
Apply conditional styling (colors, fonts, sizes) based on values from queries or application state.
**Example:** In an employee directory with a user list, you can display different background colors on **Table** cells based on whether the user is active or inactive.
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/style.png" alt=" button disable "/>
### Form Validation
Enable or disable submit buttons based on the validity of form inputs.
**Example:** In **Forms**, you can enable the Submit button only when all required fields are correctly filled.
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/form.png" alt=" button disable "/>
### Conditional Visibility
Show or hide components based on specific conditions.
**Example:** In an employee directory application, within the personal details **Form**, you can conditionally display a **Text Input** for entering a custom country name when the user selects “other” from the country dropdown.
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/conditional_visibility.png" alt=" button disable "/>
Using the **fx** editor, you can easily add dynamic behavior to your applications with minimal code.

View file

@ -0,0 +1,128 @@
---
id: managing-variables
title: Managing Variables
---
Variables in ToolJet allow you to store and manage temporary data across pages or within a single page. Theyre useful for passing values between components, persisting state, and building dynamic applications.
This guide explains how to manage [variables](/docs/beta/app-builder/events/use-case/variables) using code in your applications.
## Set, Get, and Unset Variables
Setting, getting, and unsetting variables lets you control the state of a variable. Use set to create variables or update their values, get to access them in components or queries, and unset to delete them.
### Set Variables
To set a variable in an application using code in a RunJS or RunPy query, use the `setVariable` function and pass the variable name and value.
```js
actions.setVariable("<variableName>", "<variableValue>")
```
**Example:** If youre building an internal tool for order management and want to store the *orderId* of a newly created order. You can use the following code:
```js
actions.setVariable('currentOrderId', 'ORD-10293')
```
Similarly, if you want to set a page variable use the `setPageVariable` function:
```js
actions.setPageVariable("<variableName>", "<variableValue>")
```
**Example:** If you want to set a page variable named *userPreferences*, with an object containing all the user preferences, like `{theme:'dark', language:'en'}`, you can use the following code:
```js
actions.setPageVariable('userPreferences', { theme: 'dark', language: 'en' });
```
### Get Variables
To access variables you can use the `getVariable` and `getPageVariable` functions. These functions take one argument: the name of the variable.
```js
// To get app-level variable
actions.getVariable("<variableName>");
// To get page-level variable
actions.getPageVariable("<variableName>");
```
**Example:** If you previously stored a variable named *currentOrderId* and now want to access it, You can use the code below:
```js
const orderId = actions.getVariable('currentOrderId');
```
### Unset Variables
To unset(delete) a variable, you can use the `unsetVariable` and `unsetPageVariable` functions. These functions take one argument: the name of the variable.
```js
// To delete app-level variable
actions.unsetVariable("<variableName>")
// To delete page-level variable
actions.unsetPageVariable("<variableName>")
```
**Example:** If you want to unset a page variable named *userPreference*, you would write:
```js
actions.unsetPageVariable('userPreferences');
```
## Use Cases
### Sharing Data Across Pages
You can share data across different pages by setting a variable on one page and accessing it on another.
For instance, in a content management system, the homepage might display a list of posts (as shown in the image below). When a user clicks the **View Post** button, theyre taken to a new page to see the full content. To enable this, the *postId* is stored as a global variable so it can be accessed on both the homepage and the post details page.
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/manage_var_cms.png" alt=" CMS Page"/>
On the homepage, you could add a click event handler to the **View Post** button that sets a variable called *selectedListViewIndex* with the ID of the selected post. Then, on the second page, you could retrieve this variable and use it to fetch the full post from the database.
<img className="screenshot-full img-l" style={{marginBottom:"15px"}} src="/img/app-builder/custom-code/manage_var_cms_inspector.png" alt="CMS variable"/>
```js
// Saving the post ID to a variable
actions.setVariable("selectedListViewIndex", components.postList.selectedRow.id);
// Retrieving the post ID
const postId = actions.getVariable("selectedListViewIndex");
```
### Setting Up Form Payload for a Multi-Step Form
If youre building a multi-step **Form**, each step may require different fields and appear on separate pages. You can use variables to construct the payload based on the currently active page.
Lets say your **Form** has three steps: personal details, educational background, and work experience. Each step has its own set of fields. If you want to construct a final payload to be sent as the body when the submit button is clicked on the last step, you can create a RunJS query that checks which step is active and constructs the payload accordingly. Heres how you might implement this:
```js
let payload = {};
if (page.handle === "personalDetails") {
payload.firstName = components.firstName.value;
payload.lastName = components.lastName.value;
payload.email = components.email.value;
}
else if (page.handle === "education") {
payload.educationLevel = components.educationLevel.value;
payload.major = components.major.value;
payload.graduationYear = components.graduationYear.value;
}
else if (page.handle === "workExperience") {
payload.companyName = components.companyName.value;
payload.startDate = components.startDate.value;
payload.endDate = components.endDate.value;
payload.jobTitle = components.jobTitle.value;
}
actions.setVariable("formPayload", payload);
```
You can now pass this payload to a query that sends it to your backend API endpoint.
Variables help maintain data across pages, while page variables help manage localized, page-specific logic. Use page variables for temporary, page-specific UI state, and use app-level variables when data must persist across multiple pages.

View file

@ -0,0 +1,145 @@
---
id: transform-data
title: Transforming Data
---
Data isnt always available in a ready-to-use format from a single source. Often, youll need to transform or combine data before displaying it. Common use cases include:
- Merging results from multiple data sources
- Restructuring API responses before referencing them to components
- Applying business logic such as filtering, sorting, or grouping
- Formatting fields like dates, currency values, or nested JSON objects
If you need to transform data from a single query, you can use the [transformation](/docs/beta/app-builder/connecting-with-data-sources/transforming-data) option within the query.
<img className="screenshot-full img-full" style={{ marginBottom:'15px'}} src="/img/app-builder/connecting-with-datasouces/transformation_js.png" alt="App Builder: query transformations"/>
However, if your use case involves combining data from multiple queries or components, youll need to use RunJS or RunPy queries.
For example, let's say you're building an inventory management application and want to display a list of items along with their current stock levels. You have inventory data stored in a PostgreSQL database and product data coming from a ToolJet Database. To display the item names alongside their current stock levels, you would need to merge the results from these two queries using a RunJS query as shown below.
<img className="screenshot-full img-full" style={{ marginBottom:'15px'}} src="/img/app-builder/custom-code/transformation_with_code.png" alt="App Builder: query transformations"/>
ToolJet allows you to write RunJS or RunPy queries to perform these transformations without requiring changes to your backend. This guide demonstrates how to transform data for real-world use cases.
## How It Works
ToolJet allows you to access data from:
- Configured data source queries (e.g., PostgreSQL, REST APIs, MongoDB, etc.)
- Component values (like inputs, dropdowns, tables)
With RunJS or RunPy queries, you can write code to manipulate data from multiple sources.
## Use Cases
### 1. Merging Data from Two APIs
Let's say you want to show a list of users with their order counts. The user data comes from a REST API, and the order data comes from a MySQL database. Now, if you want to show a combined list of users along with their respective order counts, you can use a RunJS or RunPy query to combine the results:
```js
// Assuming getUsers and getOrders are already defined as queries
// Run queries to fetch users and their orders
await queries.getUsers.run();
await queries.getOrders.run();
// Retrieve data from both queries
const userList = queries.getUsers.getData(); // Array of user records
const orderList = queries.getOrders.getData(); // Array of order records
// Enrich each user with their corresponding order count
const usersWithOrderCount = userList.map(user => {
// Find all orders placed by the current user
const userOrderHistory = orderList.filter(order => order.userId === user.id);
return {
...user,
orderCount: userOrderHistory.length
};
});
return usersWithOrderCount;
```
<details id="tj-dropdown">
<summary>Data from getUsers query</summary>
```js
[
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" },
{ id: 3, name: "Charlie", email: "charlie@example.com" },
{ id: 4, name: "David", email: "david@example.com" },
{ id: 5, name: "Eva", email: "eva@example.com" },
{ id: 6, name: "Frank", email: "frank@example.com" }
]
```
</details>
<details id="tj-dropdown">
<summary>Data from getOrders query</summary>
```js
[
{ id: 101, userId: 1, total: 120.00 },
{ id: 102, userId: 1, total: 45.50 },
{ id: 103, userId: 2, total: 89.99 },
{ id: 104, userId: 1, total: 60.00 },
{ id: 105, userId: 3, total: 150.00 },
{ id: 106, userId: 3, total: 200.00 },
{ id: 107, userId: 4, total: 75.00 },
{ id: 108, userId: 5, total: 50.00 },
{ id: 109, userId: 4, total: 90.00 }
]
```
</details>
<details id="tj-dropdown">
<summary>Data from usersWithOrderCount query</summary>
```js
[
{ id: 1, name: "Alice", email: "alice@example.com", orderCount: 3 },
{ id: 2, name: "Bob", email: "bob@example.com", orderCount: 1 },
{ id: 3, name: "Charlie", email: "charlie@example.com", orderCount: 2 },
{ id: 4, name: "David", email: "david@example.com", orderCount: 2 },
{ id: 5, name: "Eva", email: "eva@example.com", orderCount: 1 },
{ id: 6, name: "Frank", email: "frank@example.com", orderCount: 0 }
]
```
</details>
Now you can reference this data in your app, for instance, in a **Table** component.
### 2. Grouping and Sorting Data with Custom Business Logic
Let's say you have a list of products and want to group them by category and sort each group by stock (highest to lowest). This helps display organized inventory in a component like a nested list or grouped table. You can use a RunJS query to transform the data:
```js
// Trigger the query and retrieve the data
await queries.getProducts.run();
const products = queries.getProducts.getData();
const grouped = {};
products.forEach(product => {
const category = product.category;
if (!grouped[category]) {
grouped[category] = [];
}
grouped[category].push(product);
});
// Sort each category group by stock in descending order
for (const category in grouped) {
grouped[category].sort((a, b) => b.stock - a.stock);
}
return grouped;
```
This is how you can use RunJS or RunPy queries to transform data in ToolJet. Keep in mind that while writing code offers flexibility, it can also introduce complexity. Always consider performance implications when writing complex transformations.

View file

@ -0,0 +1,121 @@
---
id: custom-theme
title: Custom Theme
---
ToolJet supports Custom Themes at the workspace level, giving teams complete control over the look and feel of their applications. With this feature, you can define and manage multiple themes and apply them across your apps in a consistent and reusable way.
Each workspace can have one or more themes configured, and any application within that workspace can use any of the defined themes. This helps in maintaining visual consistency across applications, improves brand alignment, and enhances user experience.
## What is a Custom Theme?
A Custom Theme allows you to customize the UI components of your ToolJet apps by configuring a set of visual styles such as:
- Brand Colors (Primary, Secondary, Tertiary)
- Text colors
- Border and surface styling
- System state colors (e.g., error, success)
<img className="screenshot-full img-full" src="/img/app-builder/custom-theme/custom-theme-config.png" alt="Configure custom theme" />
You can configure these settings for both light and dark modes, and instantly preview changes using the built-in preview panel.
Each theme includes configurable values like:
- Primary: Used for buttons, links, focus states, and other interactive elements.
- Secondary (optional): For additional visual hierarchy.
- Tertiary (optional): Useful for complex color relationships.
Once saved, the theme becomes available for use in any app under the workspace.
<img className="screenshot-full img-full" src="/img/app-builder/custom-theme/custom-theme.png" alt="Configure custom theme" />
## Why Use Custom Themes?
Custom Themes empower your organization by:
- Brand consistency: Align your internal tools with your companys visual identity.
- Reusability: Define once, use across multiple apps.
- Customization: Update the look of all your apps in one go by editing the theme.
- Collaboration: Teams working on different apps can maintain a unified design system.
This is especially useful for teams with apps across different environments (e.g., internal tools, client-facing apps, admin panels) where each might need a slightly different yet consistent visual identity.
## How to Use Custom Themes
Using Custom Themes in ToolJet involves two simple steps — **creating the theme** and **applying it to your applications**.
### 1. Create a Custom Theme
- Go to your **Workspace Settings**.
- Click on the **Custom Theme** tab.
- Click **Create new theme**.
- Configure your theme styles:
- Set your **Brand colors**: Primary, Secondary, Tertiary
- Define **Text**, **Border**, **System status**, and **Surface** colors
- Choose styles for both **Light** and **Dark** modes
- Click **Save** once you're done.
You can create multiple themes as per your needs — for different teams, environments, or clients.
### 2. Apply the Theme to Your Application
- Open the app where you want to use the theme.
- Click the **Settings icon** in the **left sidebar** to open **Global Settings**.
- Scroll down to the **Theme** section.
- Youll see a dropdown showing the currently selected theme (usually the default).
- Click the dropdown to view and select from all your configured themes.
<img className="screenshot-full img-m" src="/img/app-builder/custom-theme/select-theme.png" alt="Configure custom theme" />
Once selected, your app will now use the chosen theme as the base style for components.
### 3. Use Theme Styles in Your Components
To make your components adopt the theme styles:
- Select a component in the app canvas.
- Go to the **Style** tab of the component.
- Wherever color can be set (background, border, text), youll see a **Theme** option next to the color picker. Once selected you'll see the list of theme colors such as Brand/Primary, Brand/Secondary, Text/Primary, etc.
These options map directly to what you configured during theme setup.
<img className="screenshot-full img-full" src="/img/app-builder/custom-theme/choose-theme-color.png" alt="Configure custom theme" />
Once components are styled using theme options, changing the theme from Global Settings will instantly update all those components, making your app visually consistent and easy to update.
## Scenarios
Here are some scenarios where custom themes shine:
- Brand-specific apps: Create different themes for different brands/clients your company serves.
- Dark & light mode toggle: Provide a seamless visual switch for end-users between light and dark modes.
- Multi-team organizations: Let each team within your org create and maintain their own theme without affecting others.
## Example
Heres an example showing how an application interface looks before and after applying a custom theme.
### Before (Default Theme)
This is the default ToolJet UI without any custom theme applied. It uses the standard branding and neutral colors.
<img className="screenshot-full img-full" src="/img/app-builder/custom-theme/default-theme-app.png" alt="Configure custom theme" />
### After (Custom Theme Applied)
<img className="screenshot-full img-full" src="/img/app-builder/custom-theme/custom-theme-app.png" alt="Configure custom theme" />
This is the same application after applying the "Coral" custom theme. Notice the button color, primary accents, and overall visual alignment now reflect the chosen palette.
By simply configuring a theme once at the workspace level, you can instantly apply a fresh look across all apps, improving usability, clarity, and brand identity.

View file

@ -0,0 +1,76 @@
---
id: inspector
title: Inspecting Values
---
ToolJet's Inspector is a built-in utility that provides real-time visibility into the data and state of your application. With Inspector, developers can quickly diagnose issues, understand the flow of data, and ensure that all components, queries, and variables are functioning as intended.
Inspector is accessible via the left sidebar in the App Builder. It is divided into six main sections, each offering a different perspective into your applications runtime environment:
- [Queries](#queries)
- [Components](#components)
- [Globals](#globals)
- [Variables](#variables)
- [Page](#page)
- [Constants](#constants)
## Accessing Inspector
Values that are displayed inside the inspector can be referenced in other components and queries to create interactive applications. You can access the current state of components, queries, variables, page handle, and more directly from the inspector.
You can refer to a value using the dot notation (e.g., `{{components.numberInput1.value}}`), or hover over any property in the inspector to copy its reference path. This makes it easy to connect components, reuse data, and configure logic throughout your application without writing extra code.
For example, let's say you have a **Table** displaying a list of users, and you want to fetch the details of a particular user when they are selected. You can refer to the selected row's data using the reference path in the Inspector.
You can either type this path manually or hover over the property in the Inspector to copy its path directly. This path can then be used in your query to refer to the value. Additionally, you can add an event handler to the table to automatically run this query whenever a user is selected.
<img className="screenshot-full img-s" src="/img/app-builder/debugging/inspector/copy-path.png" alt="Events Architecture Diagram"/>
### Queries
Under the Queries section, you can inspect the specifics of any query youve created. The data for a query is only visible after the query has been executed. This allows you to verify the output and troubleshoot any issues with data retrieval or manipulation. The Inspector exposes the following properties for each query:
- **isLoading** A boolean indicating whether the query is currently in progress. This can be used to control the loading state of components that depend on the query's result.
- **data** The transformed data returned by the query.
- **rawData** The original response fetched from the data source.
- **id** A unique identifier automatically assigned to every query in ToolJet.
Refer to the [Binding Data with Component](/docs/beta/app-builder/connecting-with-data-sources/binding-data-to-components) guide to learn how to bind the query data to the component.
### Components
The Components section provides a detailed view of each component present on your apps canvas. You can see the current state, properties, and values of each component, helping you understand how data flows through your application and how components interact with each other. Only the components of the current page are visible in this section.
Each component exposes a different set of states and CSAs based on its functionality. For example:
- A **Text** component exposes a `text` state and a `setText` CSA.
- A **Checkbox** component exposes a `label` state and a `setValue` CSA.
To learn more about a specific component and its exposed properties, refer to the [individual component](/docs/beta/app-builder/building-ui/component-library) guide.
Refer to the [Accessing Component State](/docs/beta/app-builder/building-ui/component-state) guide to learn how to use component state.
### Globals
By using the Globals properties in the Inspector, you can view various details about your application and its environment, such as:
- **Current User** - Information about the logged-in user, including email, name, avatar, groups, roles, and SSO details. Useful for building role-based UI or showing personalized content.
- **Environment** - Indicates the current ToolJet environment — development, staging, or production.
- **Mode** - Indicates whether the app is opened in the editor or not.
- **Theme** - Refers to the active UI theme (light or dark). You can use this to dynamically style components based on the selected theme.
- **URL Params** - These are query parameters appended to the page URL, commonly used to pass data between pages.
### Variables
The Variables section in the Inspector lets you view all app-level variables available within the current application. These variables can be used to store and share data across components and queries. You can inspect their current values here, making it easier to debug and manage dynamic behavior in your app.
### Page
The Page section displays page-specific properties (such as page handle and name) and page-level variables. Unlike app-level variables, page-level variables are only accessible within their respective pages.
- **handle** - The page handle is a unique identifier used to generate a shareable URL for the page. It is appended as a slug to the end of your application URL.
- **id** - A unique identifier automatically assigned to every page in ToolJet.
- **name** - The display name of the page, shown in the app's navigation menu. Set by the user.
- **variables** - List of page level variables in the key-value pair.
### Constants
Workspace Constants are predefined values that you can use across different applications within your workspace. They are useful for storing frequently used data such as API URLs, configuration settings, or sensitive information like API keys and database credentials. In the inspector, you can view all constants as key-value pairs, secret constant values are masked for security.

View file

@ -0,0 +1,65 @@
---
id: understanding-logs
title: Understanding Logs
---
Debugging is a critical part of building applications, and ToolJet makes it easier with a built-in Debugger that tracks and displays logs in real time. Whether its a query success or failure, or a component malfunction, the debugger logs will help you get to the root of the problem quickly.
This guide walks you through making the most of ToolJets logs, with practical examples that explain why they matter and how to use them effectively.
## How Logs Work
ToolJet captures real-time events and organizes them in the debugger panel.
When youre in the app-builder, you can look for the Debugger icon on the left sidebar. Click it to open the debugger panel. This panel becomes your main console for inspecting the query execution results (success/failure)
and component-level issues.
<img className="screenshot-full img-s" src="/img/app-builder/debugging/error-logs/debugger.png" alt="Events Architecture Diagram"/>
## Custom Logs
In ToolJet, you can use custom log methods to capture errors, debug info, and runtime events in your app. These functions work similarly to JavaScripts console.log() but offer clearer intent and structured logging.
### Log Errors
Logs an error. Useful for failed API calls, exceptions, or critical issues.
```js
actions.logError("API failed");
```
### Log Information
Logs informational messages. Use for successful actions or state changes.
```js
actions.logInfo("User logged in");
```
### Log Messages
Generic log for debugging or checkpoints.
```js
actions.log("Reached step 2");
```
## Use Case
### Debugging Queries
Lets say youre building an app and have integrated a REST API to fetch products. Youve connected this query to a **Table** component, but when you run it, the data doesnt show up. To troubleshoot this, open the debugger and navigate to the Logs tab. There, youll find detailed information about the query execution, including:
- Whether the query succeeded or failed
- Any error messages returned
- The request payload and response body
- The status code returned by the server
This information helps you identify what went wrong and where to start troubleshooting.
### Troubleshooting Component Related Issues
Lets say youve fetched user data from a database using a query, and connected it to a table component. The query runs without errors, but the table still appears empty. To investigate this, open the debugger and head to the Error Logs tab. Here, youll find error logs related to the components behavior, including:
- Errors related to misconfigured properties
- Invalid expressions used in bindings
These logs help you determine whether the issue stems from component configuration or its interaction with the query result, making it easier to fix problems and get your UI working as expected.

View file

@ -0,0 +1,40 @@
---
id: event-triggers
title: Setting Up Events
---
Events define how your app should respond when a user interacts with a component or when a specific system condition is met. Whether it's clicking a **Button** component, selecting an item from a **Dropdown**, or completing a query, events let you tie in logic that makes your app interactive and reactive.
You can use event triggers to run queries, update variables, show alerts, navigate to different pages, and more. Each event can be configured to trigger one or more actions in sequence, allowing you to build complex logic flows easily. Refer to the individual [component guide](/docs/beta/app-builder/building-ui/component-library) to see the full list of supported events, and check out the [Action Reference](/docs/category/actions-reference) for all available actions.
## Configuring an Event Handler
Suppose you're building a feedback form using a **Form** component that submits user input to a database whenever the user clicks on the submit button. To achieve this, you can configure the submit button to trigger a query when clicked.
First, create a query and name it *addData*. This query inserts **Form** values into the database. Then, configure the **Button** with the following event handler:
- Event: **On Click**
- Action: **Run Query**
- Query: **addData**
<img className="screenshot-full img-l" src="/img/app-builder/events/event-handler/form.png" alt="Events Architecture Diagram"/>
This setup ensures that every time the button is clicked, the **Form** data is sent to your database.
<img className="screenshot-full img-full" src="/img/app-builder/events/event-handler/dig.png" alt="Events Architecture Diagram"/>
## Configuring Sequential Event Handler
Continuing the previous example. After submitting the form, you may want to update the UI by fetching the latest data. To do this, create a new query and name it *fetchData* that retrieves updated records from the database.
Next, configure an event handler that runs sequentially after the *addData* query succeeds:
- Event: **Query Success**
- Action: **Run Query**
- Query: **fetchData**
<img className="screenshot-full img-full" src="/img/app-builder/events/event-handler/query.png" alt="Events Architecture Diagram"/>
This setup ensures that the *fetchData* query is triggered automatically when the *addData* query completes successfully.
<img className="screenshot-full img-full" src="/img/app-builder/events/event-handler/query-dig.png" alt="Events Architecture Diagram"/>
Whether it's submitting a form, running a query, or updating your UI, events and actions let you define dynamic, logic-driven behavior without writing backend code.

View file

@ -0,0 +1,41 @@
---
id: overview
title: Overview
---
In ToolJet, you can build dynamic, logic-driven applications using Events, Actions, Variables, and Component-Specific Actions (CSAs). These features let you define how your app responds to user interactions and system events without writing any backend code. Each component has a unique set of available events and CSAs based on its functionality. Refer to the [individual component](/docs/beta/app-builder/building-ui/component-library) guides for more details.
## Events
Events are triggers that respond when certain conditions are met — either through user interaction (for example, clicking a **Button** component or selecting a **Dropdown** option) or system-level changes (for example, the completion of a query). You can configure events using event handlers on components or queries. Each handler defines the trigger and the actions that should follow.
For example, when a user clicks a **Button**, it can trigger a query to refresh data. Once that query completes, a second event can run to show a confirmation alert.
<img className="screenshot-full img-l" src="/img/app-builder/events/overview/events.png" alt="Events Architecture Diagram"/>
## Actions
Actions specify the outcome when an event is triggered. ToolJet supports a wide range of actions such as running queries, showing alerts, navigating between pages, copying text to the clipboard, and more. You can configure actions directly within event handlers or [dynamically using JavaScript](/docs/beta/app-builder/custom-code/control-components) via RunJS queries. For a complete list of available actions, refer to the [Action Reference](/docs/category/actions-reference) guide.
<img className="screenshot-full img-l" src="/img/app-builder/events/overview/actions.png" alt="Events Architecture Diagram"/>
## Component Specific Actions (CSAs)
Component-Specific Actions (CSAs) are built-in functions that allow you to control a component's state and behavior at runtime. Each component has its own set of CSAs based on its capabilities. For example, a **Text** component supports the `setText()` action, while a **Radio Button** component offers `selectOption()`.
<img className="screenshot-full img-l" src="/img/app-builder/events/overview/csa.png" alt="Events Architecture Diagram"/>
## Variables
Variables let you store and manage data either across your entire application or within specific pages. They're essential for maintaining state, controlling logic, and creating personalized user experiences.
ToolJet supports the following types of variables:
- App-level variables accessible throughout the entire app.
- Page-level variables scoped to a specific page.
<img className="screenshot-full img-s" src="/img/app-builder/events/overview/var.png" alt="Events Architecture Diagram"/>
<br/><br/>
In addition, ToolJet provides built-in [exposed variables](/docs/beta/app-builder/building-ui/component-state) for components and queries, which represent their current runtime state.

View file

@ -0,0 +1,40 @@
---
id: csa
title: Controlling Component State
---
Component-Specific Actions (CSAs) are built-in functions that allow you to control the component state and behavior in the application. Each component has its own set of CSAs based on its capabilities.
For example, a Text component supports the `setText()` action, while a Radio Button component offers `selectOption()`.
You can trigger these actions via event handlers or by using expressions in your queries. Refer to the individual [component guide](/docs/beta/app-builder/building-ui/component-library) for a complete list of supported CSAs.
## Controlling Components
Suppose you've used a **Form** component to build a feedback form and want to clear all inputs after the data is submitted to the database. ToolJet provides a `resetForm` function to help with this, which can be triggered in two ways:
- [Using an Event Handler](#using-an-event-handler)
- [Using a JS Expression in a Query](#using-a-javascript-expression-in-a-query)
### Using an Event Handler
Suppose you have a query named **addData**, which is being used to insert the form data into the database. To clear the **Form** using an event handler, add the following configuration to your **addData** query:
- Event: **Query Success**
- Action: **Control Component**
- Component: **feedbackForm** (Select your **Form** component from the dropdown)
- Actions: **Reset Form**
<img className="screenshot-full img-full" src="/img/app-builder/events/csa/resetForm.png" alt="Events Architecture Diagram"/> <br/><br/>
This setup ensures that the **Form** component is cleared automatically when the **addData** query completes successfully.
<img className="screenshot-full img-full" src="/img/app-builder/events/csa/csa-dig.png" alt="Events Architecture Diagram"/>
### Using a JavaScript Expression in a Query
Alternatively, you can reset the **Form** directly within your query by appending this JavaScript expression:
```js
await components.feedbackForm.resetForm()
```
Component-Specific Actions give you precise control over how components behave at runtime, making your applications more interactive and responsive.

View file

@ -0,0 +1,43 @@
---
id: page-nav
title: Implementing Navigation Using Actions
---
ToolJet offers page navigation out of the box. For any custom navigation needs, such as implementing a navigation bar, you can use event handlers and actions. You can also pass query parameters during navigation, making it easy to share context between pages.
## Building Custom Navigation Menu
Follow these steps to build a custom navigation menu:
1. Add a container to serve as your navigation wrapper.
2. Place icons, text or button components inside the container for each page you want to link to.
3. For each navigation item:
- Select the component (icon or text).
- Add an event handler.
- Event: **On click**
- Action: **Switch page**
- Page: *Select the target page from the dropdown*
<img className="screenshot-full img-full" src="/img/app-builder/events/page-nav/nav-bar.png" alt="Events Architecture Diagram"/> <br/><br/>
Once configured, clicking on a navigation item will take the user to the corresponding page.
<img className="screenshot-full img-full" src="/img/app-builder/events/page-nav/nav-dig.png" alt="Events Architecture Diagram"/>
## Passing Data Between Pages
Suppose you're building a ticket management system where Page 1 displays a list of all tickets, and clicking on a ticket redirects the user to Page 2, which shows the details of the selected ticket. Here's how to set it up:
1. On Page 1, display all the tickets using a **Table** component.
2. Add an event handler to the table:
- Event: **Row clicked**
- Action: **Switch page**
- Page: **Page 2** *(Select the ticket details page.)*
- Query parameters:
- **Key**: `ticketId`
- **Value**: `{{components.ticketTable.selectedRow.ticket_id}}`
3. On Page 2, design the UI to display the ticket details.
4. Use the query parameter to fetch or display the selected ticket's data:
- Reference it with: `{{globals.urlparams.ticketId}}`
This setup enables users to click on a ticket in the table and seamlessly navigate to a detailed view of that specific ticket, with the necessary data passed between pages using query parameters.

View file

@ -0,0 +1,69 @@
---
id: variables
title: Setting Variables
---
Variables let you store and manage data across your entire application. You can use them to manage component state, control logic, store user input or to create a personalized user experiences. By setting a value once in a variable, you can reuse it across different parts of your app. This makes your app easier to build and maintain, without needing to store everything in a database.
ToolJet supports two types of variables:
- **App-level variables** are available across all pages in your app. To set an app-level variable, use the `setVariable` action.
- **Page-level variables** are only available on the page where they are created. To set a page-level variable, use the `setPageVariable` action.
## Set Variables
### App Level Variable
Suppose you are building a multi-page application where, on the first page, you ask the user for their name and want to use it in the other pages. Heres how to do it:
1. Add a **Text Input** component to collect the users name.
2. Add a **Button** component to submit the name.
3. Set up this event handler on the **Button** component:
- Event: **On Click**
- Action: **Set variable**
- Key: `username` *(Variable name of your choice.)*
- Value: `{{components.usernameinput.value}}` *(Refer to the user input in the **Text Input** component.)*
<img className="screenshot-full img-full" src="/img/app-builder/events/variables/username.png" alt="Events Architecture Diagram"/> <br/><br/>
When the user clicks the **Button** component, their name will be stored in the app-level variable `username`. You can access this variable anywhere in your app with this syntax:
```js
{{variables.username}}
```
### Page Level Variable
Now, suppose you have a **Form** in your application and want to store the users contact number only on that page when they submit the **Form**. To do this, set this event handler on the **Button** component:
- Event: **On Click**
- Action: **Set page variable**
- Key: `contact` *(Variable name of your choice.)*
- Value: `{{components.feedbackForm.data.contact.value}}` *(Refer to the user input in the **Number Input** component.)*
When the user clicks on the **Button** component, their contact number will be saved in a page-level variable named `contact`. This variable can only be used on that specific page with this syntax:
```js
{{page.variables.contact}}
```
## Unset Variables
### App Level Variables
In your multi-page app, you may want to clear (unset) the `username` variable when the user clicks the **Button** component named "Finish" on the last page. To do this, set the following event handler on the **Button** component:
- Event: **On Click**
- Action: **Unset variable**
- Key: `username` *(Variable name you want to unset.)*
### Page Level Variables
In your **Form** app, you might want to clear the page-level `contact` variable when the user clicks the **Button** component named "Next Page". To do this, set this event handler on the **Button** component:
- Event: **On Click**
- Action: **Unset page variable**
- Key: `contact` *(Variable name you want to unset.)*
:::info
You can also manage variables using code. Refer to the [Manage Variables](/docs/app-builder/custom-code/managing-variables) guide for more information.
:::

View file

@ -0,0 +1,61 @@
---
id: importing-exporting-applications
title: Import and Export Applications
---
This documentation explains the process of exporting and importing applications in ToolJet.
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Exporting Applications
- Navigate to the dashboard.
- Click on the settings icon located in the top right corner of the application.
- Click on the **Export app** button.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/import-export-apps/export-app-button-v2.png" alt="Export App Button" />
</div>
- If you select `Export All`, all the versions of the application will be exported in JSON format. If you select `Export selected version`, only the selected version will be exported in JSON format.
- Ticking the `Export ToolJet table schema` checkbox will also export the related ToolJet Database table schemas with your application. In this case, when you import the application in a workspace, the related ToolJet Database tables will also be created.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/import-export-apps/export-options-v2.png" alt="Export App Options" />
</div>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Importing Applications
- Navigate to the dashboard.
- Click on the ellipses on the **Create new app** button and select `Import`.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/import-export-apps/import-button-v2.png" alt="Import App Button" />
</div>
- After clicking on `Import`, choose the relevant JSON file that you previously downloaded during the application export process.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/import-export-apps/select-app-to-import.png" alt="Select App To Import" />
</div>
</div>
## Module Behavior During Application Import and Export
**Import**:
- When you import an application, the platform automatically checks for any existing modules with matching names in your workspace or instance. If a module with the same name already exists, the imported application connects to the existing module, avoiding duplication.
- However, if no matching module is found, the platform creates a new module from the imported JSON file.
- This approach ensures that your application imports smoothly while maintaining consistency and preventing redundant modules.
**Export**:
- When you export an application, all associated modules linked to the application are automatically included in the export.
- This ensures that any reusable components or features built as modules are preserved and can be seamlessly imported along with the app into any other workspace.

View file

@ -0,0 +1,125 @@
---
id: runjs
title: Using RunJS
---
ToolJet allows you to use external JavaScript libraries such as Compromise for natural language processing or PapaParse for parsing CSV data in your application using RunJS queries. This helps you avoid writing complex logic from scratch.
This guide walks you through the process of importing and utilizing these libraries effectively.
## Choosing Libraries
You can import various JavaScript libraries using their Content Delivery Network (CDN) links. Find the CDN links for your desired open-source projects on [jsDelivr](https://www.jsdelivr.com/).
## How to Import Libraries
Lets walk through how to import libraries using RunJS. For example, well use:
- [Compromise](https://github.com/spencermountain/compromise): for natural language processing
- [PapaParse](https://www.papaparse.com/): for parsing CSV data
### Create a RunJS Query
Open the query panel and create a new **RunJS** query.
### Add the Following Code Snippet
```js
// Function to add script dynamically
function addScript(src) {
return new Promise((resolve, reject) => {
const scriptTag = document.createElement('script');
scriptTag.setAttribute('src', src);
scriptTag.addEventListener('load', resolve);
scriptTag.addEventListener('error', reject);
document.body.appendChild(scriptTag);
});
}
try {
await addScript('https://cdn.jsdelivr.net/npm/compromise@13.11.3/builds/compromise.min.js');
await addScript('https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js');
await actions.showAlert("success", "Compromise and PapaParse imported");
} catch (error) {
console.error(error);
}
```
After adding the code, click on the **Run** button in the query panel. An alert will appear with the message "Compromise and PapaParse imported".
<img className="screenshot-full img-full" src="/img/app-builder/custom-code/import_library.png" alt="Use FlattenJS" />
:::tip
Enable the **Run this query on application load** option in the query settings to make the libraries available throughout the application as soon as the app is loaded.
:::
## Use Cases
Lets look at how you can apply these libraries in real-world use cases.
### Extracting Action Items from Meeting Notes using Compromise (NLP)
Let's say you are building an internal project management tool where users paste raw meeting notes. You want to auto-extract action items, dates, and team names. You can use the following code to process the notes using NLP:
```js
const notes = nlp("Met with John, Priya, and Marcus from the marketing team on Thursday. Discussed launch strategy for the Q3 campaign. Priya will draft the blog post by next Tuesday. John to prepare budget estimates. Marcus will handle email outreach by Friday. Next sync on July 10th.");
const people = notes.people().out('array');
const actions = notes.sentences().filter(s => s.has('#Verb')).out('array');
return { people, actions };
```
Preview the output in the query manager or click **Run** in the query panel to see the output as shown below.
<img className="screenshot-full img-full" src="/img/app-builder/custom-code/extract_tags.png" alt="Use compromise" />
### Bulk Upload Employee Data into an Employee Directory
Lets say your HR team maintains employee records in spreadsheets and wants a way to import this data quickly into your internal Employee Directory application. You can use the following code to clean up the data:
```js
const csvData = components.filepicker1.file[0].content;
const parsedData = Papa.parse(csvData, {
header: true,
skipEmptyLines: true
});
return parsedData;
```
<img className="screenshot-full img-full" src="/img/app-builder/custom-code/csv_parse_js.png" alt="Use Compromise" />
## Built-in JavaScript Libraries
ToolJet comes with some essential JavaScript libraries preloaded in the RunJS environment, so you dont need to import them manually:
- [Moment.js](https://momentjs.com/docs/) for date/time formatting and manipulation
- [Lodash](https://lodash.com/docs/) for working with arrays, objects, and collections
- [Axios](https://axios-http.com/docs/intro) for making HTTP requests
You can use these libraries directly in RunJS to simplify your logic, transform data, or integrate with APIs.
Example:
```js
// Format Timestamps for UI Display
const raw = '2025-06-05T15:42:00Z';
return moment(raw).format('MMM D, YYYY, h:mm A');// "Jun 5, 2025, 9:12 PM"
// Deep Comparison of Records with Lodash
const a = { name: 'Alice', dept: { id: 1, name: 'HR' } };
const b = { name: 'Alice', dept: { id: 1, name: 'HR' } };
return _.isEqual(a, b); // true
// Posting JSON Data with Error Handling
axios.post('https://api.company.com/inventory', {
name: 'Laptop',
quantity: 10,
}).then(res => res.data)
.catch(err => console.error(err.response.data));
```
Use RunJS to easily import and leverage external JavaScript libraries in your ToolJet app for advanced data processing and logic.

View file

@ -0,0 +1,126 @@
---
id: runpy
title: Using RunPy
---
In this guide, we will learn to import Python libraries in your applications.
If you are new to using RunPy queries, check out our [guide](/docs/data-sources/run-py/) on how to get started with RunPy. ToolJet supports installing libraries using **micropip**. Check out [this](https://pyodide.org/en/stable/usage/packages-in-pyodide.html) documentation for a list of supported libraries.
## Installing Python Packages
In ToolJet, you can write Python code for custom logic, and for intensive data processing tasks, you can use Python libraries without needing to write complex code from scratch. Heres how you can use them:
You can use **micropip** to install packages like Pandas and NumPy as follows:
```python
import micropip
await micropip.install('pandas')
await micropip.install('numpy')
```
Trigger this RunPy query once to install these packages.
<img className="screenshot-full img-full" src="/img/app-builder/custom-code/install_py.png" alt="Installing py modules" />
:::tip
Enable the **Run this query on application load** option in the query settings to make the libraries available throughout the application as soon as the app is loaded.
:::
## Use Cases
### Parse CSV Data
Lets say you want users to upload a CSV and view the parsed output. Heres how you can use pandas and Pythons CSV module. Create a RunPy query to parse CSV data using `StringIO`, `csv`, and `Pandas` module.
```python
from io import StringIO
import csv
import pandas as pd
scsv = components.filepicker1.file[0].content
f = StringIO(scsv)
reader = csv.reader(f, delimiter=',')
df = pd.DataFrame(reader)
print(df.info())
print(df)
```
<img className="screenshot-full img-full" src="/img/app-builder/custom-code/parseCSV.png" alt="Installing py modules" />
- Add a File Picker to your app and change the file type to CSV.
- In the File Pickers event settings:
- Event: On File Loaded
- Action: Run Query → choose your RunPy script
- Upload a CSV file. When you trigger the RunPy query, it will parse the data and output it in the browser console
### Prompt Preprocessing for AI APIs
When building apps that integrate with AI APIs (like OpenAI, Cohere, or HuggingFace), you often need to send long-form text inputs—like meeting transcripts, user feedback, or document excerpts to the API. However, many AI APIs have input size limitations (e.g., 4,096 tokens for GPT-3.5), and they often work best when the input is clean and concise.
So, before sending the data, you may want to:
- Clean and normalize the text (remove line breaks, extra spaces, non-ASCII characters)
- Chunk the text into API-safe sizes (e.g., 500 characters or 300 words)
- Optionally, remove irrelevant sections (like headers, boilerplate, or disclaimers)
Here's an example of how to do this preprocessing step using regular expressions (`re`):
```python
import re
# Get raw text from a multi-line input component (like a long form or a textarea)
raw_text = components.textarea1.text
# 1. Clean the text
cleaned = re.sub(r"\s+", " ", raw_text).strip()
# 2. Chunk the cleaned text into slices of 500 characters each
chunks = [cleaned[i:i+500] for i in range(0, len(cleaned), 500)]
# Output the cleaned and chunked data
print({"chunks": chunks})
```
<details id="tj-dropdown">
<summary>Input - Meeting notes</summary>
We discussed the Q3 roadmap and agreed to prioritize performance improvements. There were also suggestions to improve the onboarding experience.
Action items:
- Alice will investigate caching issues and report back by next Monday.
- Bob will look into UI responsiveness across different screen sizes.
- Carol will start planning for the user feedback survey in Q4.
Additional Discussion:
- A proposal was made to reduce build times by moving to a newer CI/CD system.
- Concerns were raised about backend API reliability and latency issues.
- Data team mentioned they are behind on setting up the new dashboard pipeline.
Next Steps:
- Weekly check-ins will resume starting next Tuesday.
- Each team will submit a biweekly progress report.
- Planning for the product demo scheduled for November 15th will start next week.
</details>
<details id="tj-dropdown">
<summary>Output - Chunked data</summary>
```json
{
"chunks": [
"We discussed the Q3 roadmap and agreed to prioritize performance improvements. There were also suggestions to improve the onboarding experience. Action items: - Alice will investigate caching issues and report back by next Monday. - Bob will look into UI responsiveness across different screen sizes. - Carol will start planning for the user feedback survey in Q4.",
"Additional Discussion: - A proposal was made to reduce build times by moving to a newer CI/CD system. - Concerns were raised about backend API reliability and latency issues. - Data team mentioned they are behind on setting up the new dashboard pipeline. Next Steps: - Weekly check-ins will resume starting next Tuesday. - Each team will submit a biweekly progress report. - Planning for the product demo scheduled for November 15th will start next week."
]
}
```
</details>

View file

@ -0,0 +1,41 @@
---
id: create-module
title: Creating a Module
---
A module is a reusable interface that can be imported into applications. It allows you to build complex functionality once and reuse it across multiple applications without having to rewrite code each time. This guide explains how to create a module in ToolJet.
Follow these steps to get started with creating a module:
- Go to the **Modules** section from the ToolJet dashboard.
<!-- <img className="screenshot-full img-full" src="/img/app-builder/modules/dashboard.png" alt="Dashboard" /> -->
- Click on the **Create Module** button. In the popup, enter a name for the module.
<img className="screenshot-full img-s" src="/img/app-builder/modules/create-module-modal.png" alt="Create Module" />
- Add components, queries, and actions just like you would in a normal app. place and resize your components on the **Module container**.
<img className="screenshot-full img-full" src="/img/app-builder/modules/module-builder.png" alt="Module Builder" />
- Click on the module container to open the properties panel Here you can see the **Input** and **Output** that help in defining how the module communicates with the parent application. These settings define how the module communicates with the parent app, making it easier to build dynamic, reusable modules that work across different data sets and queries.
<img className="screenshot-full img-s" src="/img/app-builder/modules/module-container-property.png" alt="Properties Panel" />
<!-- ## Example
Suppose you're building a **Form** module that needs to communicate with its parent app. You can do this by configuring inputs to receive data from the parent app and outputs to send data back.
Heres how it works:
- **Inputs**: Send data or trigger queries from the parent app into the module.
<img className="screenshot-full img-m" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/module-input.png" alt="Module Input" />
- **Outputs**: Send data back from the module to the parent app.
<img className="screenshot-full img-m" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/module-output.png" alt="Module Output" />
- **Test Input**: Use this to test your input configuration before importing the module into an app.
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/module-test.png" alt="Test Input" /> -->
Please refer to the **[Input and Output](/docs/beta/app-builder/modules/input-output)** documentation for detailed information on how to manage the inputs and outputs of a module.

View file

@ -0,0 +1,79 @@
---
id: data-flow
title: Data Flow
sidebar_label: Data Flow
---
This section explains how data flows work between the parent application and the module.
There are two types of data flows between the parent application and the module:
- **From Parent to Module**
- **From Module to Parent**
## From Parent to Module
When you add a module to an app:
- The parent can pass input values to the module.
- These values can be used anywhere inside the module (UI, queries, logic).
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/data-flow-parent-to-module.png" alt="Data flow from parent to module" />
For example, let's say you want to pass the **userData** from the parent application to the module. Here's what happens:
```js
// Passed from parent
{
"userData": {{ queries.getUser.data }}
}
```
You can access these values in the module using the `input` object. For instance, to access the userData, you'd use `{{input.userData}}`.
```js
// Consumed in module
{{input.userData}}
```
## From Module to Parent
The module can send data back to the parent using outputs:
- Output values are evaluated inside the module.
- The parent application reads the output values using the components object.
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/from-module-to-app.png" alt="Data flow from module to parent" />
For example, let's say you have a module that submits a form and sends the submitted data back to the parent app. Here's what happens:
```js
// Sent from module
{
"submittedFormData": {{ components.form.formData }}
}
```
You can access these values in the parent application using the `components` object. For instance, to access the submittedFormData, you'd use `{{components.<moduleName>.submittedFormData}}`.
```js
// Received in parent app
{{components.<moduleName>.submittedFormData}}
```
## Query Execution Options
You have two options for managing queries in modules:
### Parent-triggered Queries
- Define queries inside the module.
- From the parent application, trigger them using module Input query.
- Use this when you want full control from the app.
<!-- For example, if you're building a form module where the parent wants to trigger submission, define the query inside the module and use the module input query option. -->
### Self-contained Queries
- Let the module handle its own queries internally (e.g., run on load or button click inside the module).
- These queries remain invisible to the parent app.
- Use this for fully encapsulated behavior.
<!-- For example, if you're building a chart module that fetches data automatically upon loading, define the query inside the module and make it self-executing. -->
Choose based on whether the parent should control the module behavior or let the module manage itself.

View file

@ -0,0 +1,51 @@
---
id: import-export-modules
title: Import and Export Modules
---
ToolJet allows you to export and import modules, making it easy to share, reuse, or migrate modules across different workspaces.
This guide walks you through the steps to export an existing module and import it into another workspace.
## Exporting Modules
- Navigate to the **Modules** tab from the dashboard.
- Click on the menu icon of the module card you want to export.
- Click on the **Export module** button.
<img className="screenshot-full img-m" src="/img/app-builder/modules/export-module-card.png" alt="Export Module Button" />
- The selected module will be exported as a JSON file.
- This file will include all the components, logic, queries, and properties defined within the module.
Once downloaded, you can use this file to import the module into any other ToolJet workspace.
## Importing Modules
- Navigate to the **Modules** tab.
- Click on the menu icon next to the **Create new module** button in the top right corner.
<img className="screenshot-full img-l" src="/img/app-builder/modules/import-module.png" alt="Import Module Button" />
- Choose the module JSON file that you previously exported.
Once imported, the module will appear in your modules list and can be used across your applications.
## Module Behavior During Application Import and Export
**Import**:
- When you import an application, the platform automatically checks for any existing modules with matching names in your workspace or instance. If a module with the same name already exists, the imported application connects to the existing module, avoiding duplication.
- However, if no matching module is found, the platform creates a new module from the imported JSON file.
- This approach ensures that your application imports smoothly while maintaining consistency and preventing redundant modules.
**Export**:
- When you export an application, all associated modules linked to the application are automatically included in the export.
- This ensures that any reusable components or features built as modules are preserved and can be seamlessly imported along with the app into any other workspace.

View file

@ -0,0 +1,100 @@
---
title: "Configuring Inputs and Outputs"
id: "input-output"
---
Modules have their own inputs and outputs which enable them to interact with the parent application. You can configure inputs and outputs of a module from the properties panel.
## Inputs
Inputs allow the parent application to send data or trigger actions inside the module. You can access inputs in the module using the **input** object.
### Input Types
- **Data**: Use this to pass values like string, number, boolean, array, or object.
- **Query**: Use this to trigger queries inside the module from the parent app.
<img className="screenshot-full img-m" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/input-type.png" alt="Input Types" />
### How to Define Inputs
In the properties panel, go to the **Inputs** section and click **Add new**. Then, provide the following:
#### Data Input
For **Data** inputs, define the following parameters:
- **Name**: A unique name for the input
- **Type**: Select **Data**
- **Default Value** (optional)
For example, to pass a form title from the parent application, create an input with the name **formTitle** and type **Data**. You can also set a default value.
<img className="screenshot-full img-m" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/module-input.png" alt="Module Input" />
To use this input in the module, use the following syntax:
```js
{{input.<input_name>}}
```
For our case, well use `{{input.formTitle}}` to access the form title in the component.
```js
{{input.formTitle}}
```
<!--
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/consume-input.png" alt="Input Consumption" /> -->
When you import this module into an application, youll see the input field in the module settings. If you set the **formTitle** value to **User Details**, the form will display that as the title.
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/module-input-in-app.png" alt="Input Settings" />
#### Query Input
For **Query** inputs, define the following parameters:
- **Name**: A unique name for the input
- **Type**: Select Query
For example, if you want to trigger a query named **submitForm** from the parent application, create an input named **submit** and select **Query** as the type.
<img className="screenshot-full img-m" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/add-query-input.png" alt="Query Input" />
To handle this input in the module, add an event handler to a component that should trigger the query. Set the action to **Run Query** and select the query as the input (e.g., submit) from the dropdown.
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/event-handler-in-module.png" alt="Event Handler" />
When you import the module into an applications, youll see the query input in the module settings. You can then select any available query in the parent application to be triggered.
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/query-from-app.png" alt="Query Input Settings" />
## Testing Inputs
You can test how a module behaves before importing it into an application in the **Test Input** section in the properties panel of the module builder. To do this, open the module's properties panel and scroll to the **Test Input** section. Enter sample values for each input.
For example, if the module has an input named **formTitle**, you can enter a sample value like **User Details** to see how it's rendered in the module.
You can also test query inputs by creating a query inside the module builder and triggering it using the defined query input.
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/test-input.png" alt="Test Input" />
## Outputs
Outputs allow the module to send data back to the parent app. You can access outputs from the module in the parent application using the components object.
For example, if you want to send submitted form data back to the parent application, create an output named **formData** and pass the form data **From** the component.
<img className="screenshot-full img-m" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/module-output.png" alt="Module Output" />
To access this output in the parent application, use the following syntax:
```js
{{components.<module_name>.<output_name>}}
```
In this case, use the following reference to access the form data.
```js
{{components.formModule.formData}}
```
<img className="screenshot-full img-full" style={{ marginBottom:'15px' }} src="/img/app-builder/modules/output-in-app.png" alt="Output Consumption" />
To explore more on how data flows between modules and apps, check out [Data Flow](/docs/beta/app-builder/modules/data-flow) guide.

View file

@ -0,0 +1,22 @@
---
title: "Overview"
id: overview
---
**Modules** in ToolJet are reusable user interfaces that bundle components, queries, actions, and logic. Think of them as mini-apps you can plug into multiple applications within the same workspace. They help eliminate duplication, ensure consistency, and speed up development—especially for repeatable patterns like headers, forms, dashboards, or table views.
Once created, modules can be reused across your workspace. Any updates you make to a module automatically reflect in every app where it's used. This ensures a single source of truth and significantly reduces maintenance effort.
## When to Use Modules
Use modules when:
- You need a shared UI element, like a customer profile or navigation bar, across multiple apps.
- Youre building repeatable flows such as approval forms, data filters, or input panels.
- You want to simplify complex logic so others can use it with minimal setup.
Instead of copy-pasting components or logic across apps, modules give you a centralized, reusable way to build features, fully configurable through inputs and outputs to fit any context.
<img className="screenshot-full img-full" src="/img/app-builder/modules/module-builder.png" alt="Module Builder" />
To get started with modules, check out the [Create Module](/docs/beta/app-builder/modules/create-module) guide. Once your module is built, you can [use it inside any app](/docs/beta/app-builder/modules/use-module) in your workspace.

View file

@ -0,0 +1,67 @@
---
id: use-cases
title: Use Cases
sidebar_label: Use Cases
---
Modules let you reuse blocks of functionality across different applications. They help you save time by reducing repetitive work and make app development faster and more consistent.
This guide explains how modules work and showcases common use cases where they can improve your development workflow.
## Common Use Cases for ToolJet Modules
Here are some scenarios where ToolJet Modules are especially useful:
### Reusable User Authentication Form
- Build a user authentication form module using the built-in login block.
- Add this module to any app that requires user login.
- Customize the design and behavior as needed for each app.
### Standard Address or Contact Input Block
- Create a reusable module with text fields for address or contact inputs.
- Use this module in apps that collect user contact details.
- Adjust the fields and validation rules to match your needs.
### File Uploader with Preview and Validations
- Build a file uploader module using the file upload block.
- Use it in apps that require file submissions.
- Set file type restrictions, size limits, and other validations in the module.
### Comment or Feedback Box
- Create a feedback module using the textarea block.
- Include it in apps where users need to leave comments or feedback.
- Customize the placeholder, character limit, and other properties.
### Order Summary with Pricing Logic
- Build an order summary module using the table block.
- Add it to apps that need to show order details and pricing.
- Configure columns, data sources, and styles within the module.
### Notification Banner with Custom Message
- Create a notification banner module using the alert block.
- Use it in apps that need to show alerts or system messages.
- Customize the text, colors, and display duration.
### Pagination and Filter Toolbar
- Build a pagination and filter toolbar module using buttons and dropdowns.
- Use it in apps that display paginated or filtered lists.
- Customize the layout, filters, and button actions.
### Embedded Chart with Dynamic Data
- Create a chart module using the built-in chart block.
- Use it in apps that need to visualize data.
- Pass dynamic datasets to the module via inputs or variables.
### Table with Sorting, Filtering, and Export
- Build a table module with sorting, filtering, and export features.
- Add it to apps that display structured data.
- Customize functionality to fit your data and user needs.

View file

@ -0,0 +1,28 @@
---
title: "Using Modules"
id: "using-modules"
---
Once a module is created, it becomes available in the **Module** section of the component panel inside the App-Builder. You can use it like any other component by dropping it on the canvas and configuring it.
### Steps to Use a Module:
1. Open the application in which you want to use a module.
2. In the component library panel, switch to the **Module** section.
3. **Drag and drop** the Module onto the canvas.
<img className="screenshot-full img-full" src="/img/app-builder/modules/use-module.png" alt="Module Builder" />
5. You can select the module, to see a list of required **inputs** (if any) defined in the module.
6. Bind the inputs to values from your data source or configure static values if needed.
7. If your module has **outputs**, you can reference them using:
```js
{{components.<module_name>.<output_name>}}
```
You can reuse the same module multiple times in a single application by dropping it multiple times on the canvas and configuring each instance with different input bindings.

View file

@ -0,0 +1,76 @@
---
id: overview
title: Overview
---
ToolJets app-builder offers an AI-native, low-code environment that helps you build and deploy internal tools quickly without extensive coding knowledge. Whether its dashboards, approval workflows, tracking systems, or scheduling apps, you can create powerful tools in minutes.
Teams across engineering, product, operations, and business can build applications with ease, following four simple steps to get up and running in minutes.
1. **Build the Interface** Design visually with drag-and-drop components.
2. **Connect Your Data** Integrate with databases, APIs, and third-party services.
3. **Make It Interactive** Add actions, events, and logic to bring your application to life.
4. **Handle Complex Logic** Use JavaScript anywhere for advanced workflows.
In this guide, youll walk through each step and see how they fit together to bring your application to life.
<div style={{textAlign: 'center', marginBottom:'15px'}}> <img className="screenshot-full img-full" src="/img/app-builder/overview/banner.png" alt="Components Preview" /> </div>
## 1. Building the Interface
Start designing your applications interface with 60+ pre-built components, from **Tables** and **Forms** to **Charts** and **Buttons**. Just drag and drop components onto the canvas, resize, reposition, and fine-tune them through the Properties Panel.
<div style={{textAlign: 'center', marginBottom:'15px'}}> <img className="screenshot-full img-full" src="/img/app-builder/overview/components.png" alt="Components Preview" /> </div>
Each component comes with built-in styling options. You can customize text, colors, visibility, and more through the Style Panel. These components are dynamic, allowing you to manage state and events just like you would in your favorite frontend framework.
## 2. Connecting Your Data
You can connect your application to multiple [data sources](/docs/data-sources/overview) including SQL, NoSQL, vector databases, APIs, spreadsheets, and cloud services. Once connected, you can fetch, update, or manipulate data using queries.
A query is an action that interacts with your data source, whether it's fetching records, filtering results, or writing data. It acts as the bridge between your UI and your data.
<div style={{textAlign: 'center', marginBottom:'15px'}}> <img className="screenshot-full img-full" src="/img/app-builder/overview/queries.png" alt="Queries Preview" /> </div>
Use the **Query Panel** to build queries, either with a form-based interface or by writing code/SQL directly. You can use them to fetch data to display in components or to push user inputs back to your database. They can run manually or be triggered using events like page load, user actions on components, or the success or failure of other queries.
## 3. Making Apps Interactive
Make your apps interactive by adding events to components, queries, and pages. Events define how your application responds to user actions or specific conditions, bringing interactivity to your application. ToolJet provides a declarative Events system, similar to JavaScript event handlers, allowing you to control application behavior without writing repetitive code.
<div style={{textAlign: 'center', marginBottom:'15px'}}> <img className="screenshot-full img-full" src="/img/app-builder/overview/events.png" alt="Events Preview" /> </div>
Events can be triggered by various actions such as a button click, form submission, page load, or the completion of a query. When triggered, you can execute actions like running a query, opening a modal, showing a notification, or navigating to another page.
You can also chain multiple events and actions together, enabling complex multi-step workflows without writing boilerplate code.
## 4. Handling Custom Logic
ToolJet makes it easy to build apps without code, but when you need more control, it offers the ability to add custom code to write your logic. You can create JavaScript or Python queries in application builder to perform calculations, transform data, trigger other queries, or update UI components. These snippets have full access to the components properties, other queries outputs, and the entire apps state, allowing you to write custom logic for any use cases like conditional behavior, data processing, or dynamic UI updates.
<div style={{textAlign: 'center', marginBottom:'15px'}}> <img className="screenshot-full img-full" src="/img/app-builder/overview/custom-code.png" alt="Custom Code Preview" /> </div>
This gives developers the ability to handle complex scenarios with code, while still leveraging ToolJets low-code environment.
## Use Cases
With ToolJet, you can build a wide range of internal tools including but not limited to:
* Inventory management system
* Purchase order tracker
* Customer onboarding portal
* Loan origination and underwriting tool
* Fraud detection dashboard
* Expense approval workflow
* Timesheet tracker
* Employee onboarding app
* Internal ticketing system
* Compliance reporting dashboard
* Sales pipeline tracker
* Digital asset management portal
From simple dashboards to complex data-driven tools, ToolJet's app-builder helps you build applications with speed and precision.

View file

@ -0,0 +1,61 @@
---
id: row-level-security
title: Setup Row Level Security
---
Row-level security in ToolJet lets you control which records a user can see or interact with, even when multiple users access the same table. This is useful when you want to restrict access to specific rows based on [custom groups](/docs/user-management/role-based-access/custom-groups/) or [default user roles](/docs/user-management/role-based-access/user-roles#default-user-roles). Row-level security is applied on the server side, ensuring the logic is secure and hidden from the client.
The below syntax fetches the groups for the current user from the server side. Groups include both custom groups and default user roles like `admin` and `end-user`.
```bash
{{globals.server.currentUser.groups}}
```
*The above syntax will work with all data sources except Run Javascript and Run Python.*
## Example: Department-Specific View Using a PostgreSQL Data Source
If you're using PostgreSQL, you can filter records by referencing the users group(s) directly in your SQL query. This ensures each user only sees data relevant to them.
Suppose you're building an internal issue tracking tool for your company. Each department (like “Engineering”, “HR”, "Marketing") logs and manages its own issues in a shared table with the below structure:
- Table name: **issue_reports**
- Columns: **id**, **title**, **status** and **department**
- Access control: Each user is assigned to department-based Custom Groups matching department names in the database.
To ensure users only see reports from their own department(s), you can use the following SQL query:
```SQL
SELECT * FROM issue_reports
WHERE department = ANY (
string_to_array('{{globals.server.currentUser.groups}}', ',')
);
```
**How This Works:**
- `{{globals.server.currentUser.groups}}` fetches the users groups securely from the server.
- `string_to_array(...)` converts the comma-separated string containing groups into a usable array.
- `department = ANY (...)` ensures users only see issues filed under their own departments.
### Filtered Results Based on Departments:
Based on the query logic, users assigned to the Engineering and HR groups will see the following issues:
| id | title | status | department |
|:---|:----------------------------------|:---------|:------------|
| 1 | Login bug on portal | Open | Engineering |
| 3 | Leave approval stuck | Open | HR |
| 4 | Data sync error | Open | Engineering |
| 5 | Employee onboarding delay | Pending | HR |
| 9 | GitHub webhook failure | Open | Engineering |
Users assigned to the Marketing group will see only the issues related to their department:
| id | title | status | department |
|:---|:----------------------------------|:---------|:---------------|
| 2 | Deliver failure issues | Pending | Marketing |
| 7 | Campaign budget approval delayed | Pending | Marketing |
| 8 | Social media calendar not updated | Open | Marketing |
This setup ensures that a shared internal tool remains secure, with minimal query changes and no duplication of logic or views, making it ideal for HR dashboards, ticketing systems, CRM tools, and more.

View file

@ -0,0 +1,56 @@
---
id: ai-docs-assistant
title: AI Docs Assistant
---
The **AI Docs Assistant** is an intelligent assistant designed to help you navigate ToolJets documentation with ease. Whether you need quick answers, step-by-step guides, or concept explanations, this assistant provides instant support by summarizing docs, troubleshooting issues, and guiding you through best practices.
You can access the AI Docs Assistant under the **Learn** tab in the Build with AI sidebar. Simply ask a question or describe what you're looking for, and the assistant will provide relevant documentation and insights.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/doc0.png" alt="tooljet ai doc assistant" />
</div>
### What You Can Do with the AI Docs Assistant
- Get guidance on setting up workspaces, managing users, and configuring roles.
- Quickly understand complex topics with clear, actionable steps
- Find answers to common questions and resolve issues efficiently.
- Explore how to connect databases, APIs, and external tools.
- Discover best practices for securing your applications.
- Get up to speed with key functionalities and platform best practices.
### Examples
1. Custom Schema in [Form](/docs/widgets/form/) component:
**Prompt**: Can you create a custom schema for a Form component with two input fields for name, phone number, and a dropdown for gender?
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/doc1.png" alt="tooljet ai doc assistant" />
</div>
2. Dynamic columns in [Table](/docs/widgets/table/table-properties/) component:
**Prompt**: Can you explain dynamic columns in the Table component?
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/doc2.png" alt="tooljet ai doc assistant" />
</div>
3. Plotly JSON in [Chart](/docs/widgets/chart/) component:
**Prompt**: Can you help me with the structure of the Plotly JSON that I can pass into Chart components?
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/doc3.png" alt="tooljet ai doc assistant" />
</div>

View file

@ -0,0 +1,58 @@
---
id: generate-applications
title: Generate Applications
---
This guide explains how to quickly generate and modify business applications using ToolJet. You can create an app from scratch with a single prompt or enhance an existing app with AI-powered assistance.
## Creating Application
To create an application, follow these steps:
1. **Enter a prompt** Describe the business application you want to build in the prompt input on the dashboard.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/generate01.png" alt="tooljet generate apps " />
</div>
2. **Accept or modify requirements** After submitting your prompt, the app will be created, and youll be taken to the App Builder, where a list of features, a database schema, design details, and query specifications will be generated based on your prompt.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/generate02.png" alt="tooljet generate apps " />
</div>
You can accept or modify these application requirements after reviewing them thoroughly.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/generate03.png" alt="tooljet generate apps " />
</div>
3. **App Generation** Once you confirm the requirements, ToolJet will build the application inside the App Builder.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/generate04.png" alt="tooljet generate apps " />
</div>
## Modifying Application
You can modify any application in ToolJet with AI assistance, whether it's a newly created app or an existing one. You can update components and queries within your application with just a prompt.
For example, if you want to add a button in your app you can write a prompt for the same.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/generate05.png" alt="tooljet generate apps " />
</div>
## Limitations
ToolJet supports generating queries with AI for the following data sources:
- [Postgres](/docs/data-sources/postgresql/)
- [MySQL](/docs/data-sources/mysql/)
- [SQL Server](/docs/data-sources/mssql/)
- [RunJS Queries](/docs/data-sources/run-js)

View file

@ -0,0 +1,40 @@
---
id: overview
title: Overview
---
With ToolJet, you can build business applications effortlessly using natural language. Whether you're starting from scratch or refining an existing app, it simplifies the process with it's intelligence.
Additionally, it comes with an AI-powered [documentation assistant](/docs/build-with-ai/ai-docs-assistant), ready to answer any questions about ToolJet's features, components, and integrations, helping you build faster.
Follow these step-by-step instructions to create an inventory management application:
1. **Describe your application** Provide a prompt detailing the business application you want to create. (Example: "Inventory management system for a manufacturing company.")
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/overview-ai-1.png" alt="tooljet ai overview" />
</div>
2. **Refine the requirements** Review and accept or modify the application requirements suggested.
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/overview-ai-2.png" alt="tooljet ai overview" />
</div>
3. **Customize your application** Use AI to customize the generated application to your specific needs, adjusting components and styles, and also data source queries.
- **Generated Application**
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/overview-ai-3.png" alt="tooljet ai overview" />
</div>
- **Customizing Application**
<div style={{textAlign: 'center', marginBottom:'15px'}}>
<img className="screenshot-full" src="/img/tooljet-ai/overview-ai-4.png" alt="tooljet ai overview" />
</div>
Refer to [Generate Applications](/docs/build-with-ai/generate-applications) and [AI Docs Assistant](/docs/build-with-ai/ai-docs-assistant) documentation to learn more.

View file

@ -0,0 +1,300 @@
---
id: tooljet-mcp
title: ToolJet MCP
---
<div className="badge badge--self-hosted">
<span>Self Hosted</span>
</div>
The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) is a standard for connecting Large Language Models (LLMs) to platforms like ToolJet. This guide covers how to connect ToolJet to AI tools using MCP, enabling your AI assistants to interact with and manage your ToolJet instance.
## What is ToolJet MCP?
ToolJet MCP is a bridge that connects AI assistants to your ToolJet platform through the Model Context Protocol. This allows AI tools to:
- Manage users and workspaces
- Access app information
- Perform administrative tasks
- Interact with your ToolJet instance programmatically
## Supported AI Tools
You can connect ToolJet to the following AI tools using MCP:
- [Cursor](#cursor)
- [Windsurf](#windsurf) (Codium)
- [Visual Studio Code](#visual-studio-code-copilot) (Copilot)
- [Cline](#cline) (VS Code extension)
- [Claude desktop](#claude-desktop)
- [Claude code](#claude-code)
## Prerequisites
Before you begin, you'll need:
1. A ToolJet instance with admin access
2. An API access token from your ToolJet instance
3. Node.js (v14 or higher)
4. An MCP-compatible AI assistant
## Getting Started
### Step 1: Get an Access Token
Get an access token from your ToolJet instance. You'll need this token to authenticate the MCP server. Refer to the [ToolJet API](https://docs.tooljet.ai/docs/tooljet-api#enabling-tooljet-api) documentation for more details on how to generate an API token.
### Step 2: Configure Your AI Tool
Follow the instructions below to configure your preferred AI tool to connect with ToolJet MCP.
#### Cursor
1. Open [Cursor](https://www.cursor.com/) and create a `.cursor` directory in your project root if it doesn't exist.
2. Create a `.cursor/mcp.json` file if it doesn't exist and open it.
3. Add the following configuration:
```json
{
"mcpServers": {
"tooljet": {
"command": "npx",
"args": [
"-y",
"@tooljet/mcp",
],
"env": {
"TOOLJET_ACCESS_TOKEN": "<your-access-token>",
"TOOLJET_HOST": "https://your-tooljet-instance.com"
}
}
}
}
```
Replace `<your-access-token>` with your ToolJet access token and update the host URL to point to your ToolJet instance.
4. Save the configuration file.
5. Open Cursor and navigate to **Settings/MCP**. You should see a green active status after the server is successfully connected.
#### Windsurf
1. Open [Windsurf](https://docs.codeium.com/windsurf) and navigate to the Cascade assistant.
2. Tap on the hammer (MCP) icon, then **Configure** to open the configuration file.
3. Add the following configuration:
```json
{
"mcpServers": {
"tooljet": {
"command": "npx",
"args": [
"-y",
"@tooljet/mcp",
],
"env": {
"TOOLJET_ACCESS_TOKEN": "<your-access-token>",
"TOOLJET_HOST": "https://your-tooljet-instance.com"
}
}
}
}
```
Replace `<your-access-token>` with your ToolJet access token and update the host URL to point to your ToolJet instance.
4. Save the configuration file and reload by tapping **Refresh** in the Cascade assistant.
5. You should see a green active status after the server is successfully connected.
#### Visual Studio Code (Copilot)
1. Open [VS Code](https://code.visualstudio.com/) and create a `.vscode` directory in your project root if it doesn't exist.
2. Create a `.vscode/mcp.json` file if it doesn't exist and open it.
3. Add the following configuration:
```json
{
"inputs": [
{
"type": "promptString",
"id": "tooljet-access-token",
"description": "ToolJet access token",
"password": true
},
{
"type": "promptString",
"id": "tooljet-host",
"description": "ToolJet host URL",
"default": "https://your-tooljet-instance.com"
}
],
"servers": {
"tooljet": {
"command": "npx",
"args": ["-y", "@tooljet/mcp"],
"env": {
"TOOLJET_ACCESS_TOKEN": "${input:tooljet-access-token}",
"TOOLJET_HOST": "${input:tooljet-host}"
}
}
}
}
```
4. Save the configuration file.
5. Open Copilot chat and switch to "Agent" mode. You should see a tool icon that you can tap to confirm the MCP tools are available. Once you begin using the server, you will be prompted to enter your access token and host URL.
For more info on using MCP in VS Code, see the [Copilot documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers).
#### Cline
1. Open the [Cline](https://github.com/cline/cline) extension in VS Code and tap the **MCP Servers** icon.
2. Tap **Configure MCP Servers** to open the configuration file.
3. Add the following configuration:
```json
{
"mcpServers": {
"tooljet": {
"command": "npx",
"args": [
"-y",
"@tooljet/mcp",
],
"env": {
"TOOLJET_ACCESS_TOKEN": "<your-access-token>",
"TOOLJET_HOST": "https://your-tooljet-instance.com"
}
}
}
}
```
Replace `<your-access-token>` with your ToolJet access token and update the host URL to point to your ToolJet instance.
4. Save the configuration file. Cline should automatically reload the configuration.
5. You should see a green active status after the server is successfully connected.
#### Claude desktop
1. Open [Claude desktop](https://claude.ai/download) and navigate to **Settings**.
2. Under the **Developer** tab, tap **Edit Config** to open the configuration file.
3. Add the following configuration:
```json
{
"mcpServers": {
"tooljet": {
"command": "npx",
"args": [
"-y",
"@tooljet/mcp",
],
"env": {
"TOOLJET_ACCESS_TOKEN": "<your-access-token>",
"TOOLJET_HOST": "https://your-tooljet-instance.com"
}
}
}
}
```
Replace `<your-access-token>` with your ToolJet access token and update the host URL to point to your ToolJet instance.
4. Save the configuration file and restart Claude desktop.
5. From the new chat screen, you should see a hammer (MCP) icon appear with the new MCP server available.
#### Claude code
1. Create a `.mcp.json` file in your project root if it doesn't exist.
2. Add the following configuration:
```json
{
"mcpServers": {
"tooljet": {
"command": "npx",
"args": [
"-y",
"@tooljet/mcp",
],
"env": {
"TOOLJET_ACCESS_TOKEN": "<your-access-token>",
"TOOLJET_HOST": "https://your-tooljet-instance.com"
}
}
}
}
```
Replace `<your-access-token>` with your ToolJet access token and update the host URL to point to your ToolJet instance.
3. Save the configuration file.
4. Restart [Claude code](https://claude.ai/code) to apply the new configuration.
## Platform-Specific Setup
### Windows Users
If you're using Windows, prefix the command with `cmd /c`:
```json
{
"mcpServers": {
"tooljet": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@tooljet/mcp",
],
"env": {
"TOOLJET_ACCESS_TOKEN": "<your-access-token>",
"TOOLJET_HOST": "https://your-tooljet-instance.com"
}
}
}
}
```
## Available Tools
ToolJet MCP provides several tools that AI assistants can use to interact with your ToolJet instance:
### User Management
| Tool | Description |
| --- | --- |
| `get-all-users` | Retrieve a list of all users in your ToolJet instance |
| `get-user` | Get detailed information about a specific user |
| `create-user` | Create a new user in a specified workspace |
| `update-user` | Update a user's profile information |
| `update-user-role` | Change a user's role within a workspace |
### Workspace Management
| Tool | Description |
| --- | --- |
| `get-all-workspaces` | List all workspaces in your ToolJet instance |
### Application Management
| Tool | Description |
| --- | --- |
| `get-all-apps` | List all applications within a specific workspace |
## Example Usage
Once connected, your AI assistant can perform tasks like:
- "Show me all users in my ToolJet instance"
- "Create a new user named John Doe in the Marketing workspace"
- "List all the apps in the Development workspace"
- "Update the role of user@example.com to Admin in the Sales workspace"
For a full list of tools available, see the [GitHub README](https://github.com/ToolJet/tooljet-mcp). If you experience any issues, [submit a bug report](https://github.com/ToolJet/tooljet-mcp/issues/new).

View file

@ -0,0 +1,5 @@
{
"label": "Contributing Guide",
"position": 11,
"collapsed": true
}

View file

@ -0,0 +1,81 @@
---
id: code-of-conduct
title: Contributor Code of Conduct
---
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at hello@tooljet.com . All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View file

@ -0,0 +1,40 @@
---
id: introduction
title: Introduction
---
Welcome to the ToolJet Documentation guide! We're thrilled to have you here and can't wait to see the contributions you'll make to our docs. Your insights and improvements will help thousands of users get the most out of ToolJet 🚀.
In the following sections, you'll find everything you need to get started with contributing to our documentation.
## What Makes Good Documentation?
Good documentation is all about clarity, simplicity, and effectiveness. It should guide users through the usage and workings of ToolJet with precision, making it as easy as possible for them to build and deploy internal tools.
Documentation is a cornerstone of our product. It's one of the most visited sections of our website, second only to our homepage. Without well-crafted, thorough documentation, users can't fully utilize the power of ToolJet, and their experience may suffer as a result.
### The Golden Rule
If a ToolJet feature lacks comprehensive documentation, it isn't considered complete. Quality documentation is essential for the success of our product and the satisfaction of our users. Its often through documentation that users discover new features or decide to upgrade their tools.
As a contributor, you can create detailed, user-centric content that thoroughly covers the features you're documenting in an engaging and accurate way.
## Types of Documentation
We aim to create rich, informative content across three key areas of documentation:
1. **ToolJet Concepts**: Basic explanations of specific topics, offering general insights into how ToolJet features work.
2. **How-to Guides**: Step-by-step instructions that help users accomplish specific tasks within ToolJet.
3. **Reference**: Concise, lookup-style documentation that covers all possible usages, definitions, and edge cases for ToolJet features.
As you document features, consider which of these categories your content fits best. Sometimes a feature might require multiple types of documentation to be fully covered.
### Measuring Impact and Gathering Feedback
The quality of our documentation isn't just determined by how well it's written—it's also measured by how well it serves our users. We regularly use analytics and feedback tools to assess the impact of our documentation and make data-driven improvements.
## Next Steps...
Once you've set up your local environment, take some time to explore our [Style Guide](/docs/contributing-guide/documentation-guidelines/style-guide), understand our page structures, and learn how to work with Docusaurus, the framework we use for our documentation.
We look forward to your contributions and are excited to see how you'll help make ToolJet documentation even better!

View file

@ -0,0 +1,15 @@
---
id: pr-checklist
title: Pull Request Review Checklist
---
Use this quick checklist to catch common issues and maintain consistency across your project.
- Review spelling and grammar using tools like ChatGPT or Grammarly.
- Ensure no compilation issues arise due to formatting.
- Verify that all h2 and h3 headings follow title case.
- Confirm that every section starting with h2 has a 24px padding-top.
- Test for broken links, missing images, and any incorrect code.
- Ensure new internal links use root-relative file paths.
- Identify opportunities for relevant cross-linking.
- Ensure that the changes are implemented in all the required versions.

View file

@ -0,0 +1,236 @@
---
id: style-guide
title: Style Guide
---
Welcome to the ToolJet's Style Guide for creating clear, consistent, and accessible documentation. In this guide, you will find recommendations on text formatting, proper use of headers, code snippet styling, accessibility practices, and much more.
## 1. Text Formatting Guidelines
Different elements in your projects should be formatted consistently for clarity. Here are some recommendations:
a. Italics are used for names given to Queries, Database Tables, and Components.
**Examples:**
- Create a new query and rename it to *getEmployees*.
- Select **ToolJetDB** as the the data source and *Employees* table as the data source.
- Pass the returned data to the *allEmployees* component.
b. Bold is applied for Workspace Constants, Clickable Buttons, fx, Data Sources, and Components.
**Examples:**
- Select the **Button** component and change its label to "Save".
- Drag andn drop a **Table** component and rename it to *todosTable*.
- Expand the query panel at the bottom and click on the **Add** button to create a new **REST API** query.
c. Use Single Ticks for Inline Code and Triple Ticks for Multi-Line Code.
**Examples:**
- The **fx** option next to the Loading state property can be used to add a loader to the component. For instance, you can enter `{{queries.getData.isLoading === true}}` to show the loader while the *getData* query is running.
- Use the below code to fetch data:
```js
// this code is wrapped in triple ticks
const fetchData = async () => {
const response = await api.get('/users');
console.log(response.data);
};
```
**Additional Items**:
- API Endpoints: Use code ticks for API endpoints (e.g., `GET /api/v1/resources`).
- Labels or User Inputs: Use double quotes to highlight labels or user inputs (e.g., "Enter your username").
---
## 2. Headings
Proper use of headers is crucial for organizing content and improving readability. Use the following guidelines to determine which header level to apply:
- **Title Casing**: Apply title casing for all headers to maintain consistency.
- **Main Header**: Use a single hash (`#`) for the main topic of the document or section. This should be used once per document for the main header.
- **Secondary Header**: Use double hashes (`##`) for subtopics or main sections within a major section. This level of header should organize content under the main header.
- **Tertiary Header**: Use triple hashes (`###`) for more detailed points or subsections under a secondary header. This header is useful for going deeper into specifics within a section.
- **Quaternary Header**: Use four hashes (`####`) for even more granular details within a tertiary section. This header is rarely needed but can be useful in complex documentation.
- **Spacing**: Ensure theres a blank line before and after each header to maintain readability and to separate the sections clearly.
- **Header Frequency**: Avoid using more than three levels of headers to prevent overcomplication. If additional granularity is needed, consider breaking the content into separate sections or documents.
---
## 3. Markdown Tables
To efficiently present extensive and repetitive information about features, such as the properties of a component, use markdown tables. This format helps organize and display the data clearly and concisely.
Ensure all tables are left-aligned for consistency. This aids in readability and ensures that the content is easy to scan.
**Example**:
| <div style={{ width:"100px"}}> Variable </div> | <div style={{ width:"200px"}}> Description </div> | <div style={{width: "200px"}}> How To Access </div>|
|:---------- | :---------- | :------------ |
| chartTitle | Holds the title of the chart component. | Accessible dynamically with JS (for e.g., `{{components.chart1.chartTitle}}`). |
| xAxisTitle | Contains the title for the X-axis of the chart. | Accessible dynamically with JS (for e.g., `{{components.chart1.xAxisTitle}}`). |
| yAxisTitle | Contains the title for the Y-axis of the chart. | Accessible dynamically with JS (for e.g., `{{components.chart1.yAxisTitle}}`). |
| clickedDataPoints | Stores details about the data points that were clicked.| Accessible dynamically with JS (for e.g., `{{components.chart1.clickedDataPoints}}`). Each data point includes `xAxisLabel`, `yAxisLabel`, `dataLabel`, `dataValue`, and `dataPercent`. |
- Use **bold** formatting for all column headers to differentiate them from the table content.
- Avoid leaving empty cells in tables. If a cell doesnt have applicable content, use a placeholder like "N/A" or "—" to indicate that the cell is intentionally blank.
---
## 4. Admonitions
Admonitions are blocks of content that are designed to draw attention to specific points in your documentation. Use them sparingly to avoid overwhelming the user. Reserve admonitions for critical or cautionary information only.
- **Warning Admonitions**: Use `warning` type admonitions for high-risk actions or irreversible changes. This type of admonition should alert users to potential dangers or critical issues.
**Example**:
:::warning
Ensure you back up your data before upgrading to the latest version.
:::
- **Tip Admonitions**: Use `info` type admonitions to offer useful hints or best practices. These are generally positive and provide additional value to the user.
**Example**:
:::info
Preview the changes before pushing them.
:::
Overuse can dilute their impact. Use *italics* instead of admonitions whenever possible to emphasize important information instead of admonitions. This is a less intrusive way to draw attention to key details.
---
## 5. Image Guidelines
Include images that closely align with real-world use cases. This makes the documentation more practical and relatable for the user.
- Name images to reflect their purpose, such as `create-get-query.jpeg`. This helps maintain an organized file structure and makes it easier to locate specific images.
- Align images to the left. This is the standard alignment that works well with most content layouts.
- Set the image width to 100% to ensure it scales appropriately with different screen sizes.
- Keep image sizes under 300kb to balance load speed and quality.
- Alt text should be a concise description of the image, providing the same information as the image itself. This is essential for accessibility and for users who rely on screen readers.
- Skip phrases like "image of" or "graphic of" as screen readers handle this automatically. Focus on describing what is important about the image.
- Use `WEBP` or `PNG` formats for web images due to their balance between quality and file size.
- Use `SVG` for logos or icons to ensure scalability without loss of quality.
---
## 6. Tone and Clarity
Maintaining a clear and consistent tone throughout your documentation is crucial for effective communication. The goal is to be concise, informative, and user-friendly.
- Keep language straightforward and concise. Avoid jargon unless it's essential for the audience and provide explanations where necessary.
- Always proofread content using Grammarly or a similar tool before submitting a PR. This helps catch errors that might be missed during the initial writing process.
- Use the active voice wherever possible to make the content more direct and engaging. Passive voice can make sentences longer and more difficult to understand.
---
## 7. Bullet Points
Use bullet points to break down steps or lists for clarity. This makes the content easier to scan and understand.
- Avoid using bullet points for a single item. If there is only one point to make, integrate it into the main text instead.
- Ensure subpoints are correctly indented in markdown. This maintains the hierarchy and relationship between the main point and subpoints.
- End bullet points that are complete sentences with a period. This helps maintain proper grammar and readability.
- Do not insert blank lines between bullet points. This keeps the list compact and visually connected.
- Use nested bullet points for items that require further explanation or hierarchy within a list.
---
## 9. Specific Language Guidelines
Use the below language guidelines to ensure clarity and consistency.
### HTTP Formatting
- All HTTP headers should be capitalized like this: `First-Letter-Capitalized`. This follows the standard convention and makes the headers easier to distinguish.
**Example**:
```
Content-Type: application/json
Authorization: Bearer <token>
```
- HTTP blocks should be ready to run when pasted into tools like Postman or `cURL` commands. This means including all necessary components like headers, body, and method. **Example**:
```bash
curl -X POST https://api.example.com/resource \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{"key": "value"}'
```
### JavaScript Guidelines
- End statements with semicolons (`;`). While JavaScript can often infer semicolons, explicitly including them prevents potential issues, especially in complex code. **Example**:
```javascript
const name = 'John';
console.log(name);
```
- Use single quotes for strings unless double quotes are necessary (e.g., to avoid escaping single quotes inside the string). **Example**:
```javascript
const greeting = 'Hello, world!';
```
### JSON Formatting
- Indent JSON by 2 spaces. This is a standard practice that improves readability. **Example**:
```json
{
"name": "John Doe",
"age": 30,
"city": "New York"
}
```
- Avoid comments in JSON code, as JSON does not natively support comments. If explanations are needed, provide them outside the JSON block in the documentation.
### Shell Scripting
- Break separate commands into distinct code blocks or chain them with `&&` for readability. For multi-line commands, use `\` to break lines. **Example**:
```bash
sudo apt-get update && \
sudo apt-get install -y curl
```
- Preface comments with `#` to explain the command's purpose.
**Example**:
```bash
# This command installs Node.js
sudo apt-get install -y nodejs
```
### SQL Queries
- Format SQL queries with keywords in uppercase, and break down long queries into multiple lines for better readability. **Example**:
```sql
SELECT name, age, city
FROM users
WHERE age > 30
ORDER BY name ASC;
```
---
## 10. Linking Guidelines
- Use root-relative paths (e.g., `/schema/postgres/tables.mdx`) instead of relative links to avoid broken links during file moves. This practice ensures that links remain functional even if files are moved within the directory structure. **Example**: <br/>
`[Postgres tables](/schema/postgres/tables.mdx)` links to the Postgres tables page.
- When linking to a specific section within a page, use anchor links to direct the user precisely where needed. **Example**: <br/>
`ToolJet supports [multiple environments,](https://docs.tooljet.ai/docs/#multiple-environments)` takes the user directly to the specific section.
---
## 11. Semantics and Terminology
- Write in the second person (e.g., *you*, *your*). This makes the content more engaging and directly applicable to the reader.
- Ensure that case sensitivity is consistently applied across the document, particularly for technical terms or commands. This is important for commands and variables in code that are case-sensitive.
**Example**: <br/>"`MyVariable` and `myvariable` are not the same."
- Define acronyms on first use and avoid using them excessively to maintain readability. This helps readers who may not be familiar with all acronyms.
**Example**: <br/>"The Content Delivery Network (CDN) is used to deliver content to users efficiently."
- Maintain consistent terminology throughout the document. If you start with "user," don't switch to "customer" later in the same context.
---
By following these guidelines, you can ensure that your documentation is clear, consistent, and easy to use for a wide range of audiences.

View file

@ -0,0 +1,69 @@
---
id: l10n
title: Localization
---
Welcome to ToolJet Localization Guide. The goal of the Localization is to make ToolJet easy to use and close to all countries, languages, and general cultural groups. On this page, you will find instructions on how to contribute to ToolJet through Localization and make a more friendly ToolJet for all regions.
## Adding Translations
- For adding the translations of your language in ToolJet, you'll need to create a new **languagecode.json** file which will include all the translations for the keywords in your language, and then list the language in the **languages.json** file for the language to be listed in the dashboard of the ToolJet.
- Go to the **frontend** directory which is at the root of ToolJet, then go to the **assets** and inside assets, you'll find the **translations** directory. You have created a new json file with the **language code** as the file name. The language code should follow [ISO 639-1 Code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
```
\frontend
|--\assets
|--\--\translations
|--\--\--\languages.json
|--\--\--\en.json
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/l10n/files.png" alt="files" />
</div>
- Let's localize ToolJet in the **French** language. Create a new json file inside the translations directory and name it **fr.json**. `fr` is the language code for French.
- After creating the new file, open the **en.json** file and copy all the contents of the file to the newly created **fr.json**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/l10n/en.png" alt="files" />
</div>
- Once copied, you can now start adding the translations for the keywords in the french language.
- After completing the translation, all you need to do is list the language in **languages.json** file. You'll need to add an object with three key-value pairs. **lang** - the name of the language that you added, **code** - the language code, and the **nativeLang** - name of language in the native.
```js
{
"languageList":
[
{ "lang": "English", "code": "en", "nativeLang": "English" },
{ "lang": "French", "code": "fr", "nativeLang": "Français" }
]
}
```
<!--
- Once you list the language in the `language.json` file, you'll be able to see it in the **dashboard**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/l10n/list.png" alt="files" />
</div>
:::info
ToolJet will automatically detect your browser's default language and will switch to it. If your browser's default language translation is not available in ToolJet then it will set English as the default language.
:::
-->
:::note
Feel free to reach us on [Slack](https://join.slack.com/t/tooljet/shared_invite/zt-2rk4w42t0-ZV_KJcWU9VL1BBEjnSHLCA) for any help related to Localization.
:::

View file

@ -0,0 +1,424 @@
---
id: creating-a-plugin
title: 'Marketplace: Creating plugins'
---
## What are plugins
ToolJets development has centered on extensibility, allowing developers to utilize plugins that expand their capabilities. Currently, these plugins are limited to connectors, including data source connectors like PostgreSQL, MySQL, Twilio, Stripe, and more. Using JavaScript/TypeScript, developers can develop plugins to enhance ToolJet's functionality and publish these plugins on the ToolJet Marketplace.
This guide will provide step-by-step instructions for creating ToolJet plugins using the `tooljet` CLI.
The `tooljet` CLI is a user-friendly command-line tool designed to simplify the plugin building process. As part of this guide, we will create a basic plugin for GitHub.
## Step 1: Creating a New Plugin - GitHub Plugin
The first step is to bootstrap a new plugin for the ToolJet marketplace. The plugin will authenticate users with a GitHub Personal Access Token and include fundamental operations such as fetching user details, repositories, issues, and pull requests.
If you have completed the **[Setup](/docs/contributing-guide/marketplace/marketplace-setup)** guide, you can begin developing the plugin using the `tooljet` CLI. To initiate plugin development, enter the following command in the terminal:
```bash
# create a new plugin
tooljet plugin create github
```
When prompted, enter the **plugin name** and select the **plugin type**, which is api in this case. Additionally, select **yes** when prompted to create a new plugin for the marketplace.
If your plugin is hosted on GitHub, please provide the **repository URL** when prompted. Otherwise, leave it blank.
When a plugin is created using the `ToolJet` CLI, an object is added to the **plugins.json** file in the **`ToolJet/server/src/assets/marketplace/`** directory. This object includes metadata about the plugin, such as its name, description, version, author, and other relevant details.
The plugins.json file serves as a registry of all available plugins for use in ToolJet. When the ToolJet server starts up, it reads the plugins.json file and loads all plugins that are listed in it.
:::info
It is important to note that the plugins.json file should not be manually edited, as it is automatically generated by the `ToolJet CLI`. Making changes to this file can result in issues with the proper functioning of the plugins in the system.
:::
All marketplace plugins are stored in the **`/marketplace`** directory of the ToolJet repository. You can find the GitHub plugin **[here](https://github.com/ToolJet/ToolJet/tree/develop/marketplace/plugins/github)**.
The structure of a typical ToolJet plugin directory appears as follows:
```bash
github/
package.json
lib/
icon.svg
index.ts
operations.json
manifest.json
```
In this structure, the file **manifest.json** contains information about the plugin's name, description, and other details. The file **operations.json** contains metadata about all the operations that the plugin supports. The main file, **index.ts**, creates a QueryService for the plugin, which handles queries, connection testing, caching, and more. The **icon.svg** file serves as the plugin's icon, while **package.json** is automatically generated by the CLI.
:::info
**Why do we need a manifest.json file or a operations.json file?**
The manifest.json file is used by a React component to create a dynamic UI for connection forms. It defines the schema of an API or data source, including its name, type, and any exposed variables, as well as options for authentication and other customizable properties. The properties section specifies the required fields and their types for connecting to the API or data source. By reading the manifest.json file, the React component generates the necessary UI components based on the schema, such as text inputs, dropdowns, checkboxes, and other elements.
On the other hand, the operations.json file contains a schema definition for a specific data source, like Github. It describes the available operations and their parameters that can be used to query the data source. A React component uses this schema to create queries in ToolJet applications, generating a UI that allows users to select the desired operation and provide the required parameters. The component uses the properties defined in the operations.json file to create various UI elements, such as dropdowns and input fields, and handle user interactions to create the final query. Once the user fills in the required parameters, the component uses them to generate a query that can be executed against the data source and return the results to the user.
Overall, *manifest.json* and *operations.json* files are essential for creating dynamic UI components in ToolJet applications. They define the schema for data sources and available operations, which React components then use to generate user-friendly UI elements. By utilizing these files, ToolJet enables users to easily connect to various APIs and data sources, perform queries, and retrieve data in an intuitive and efficient manner.
:::
## Step 2: Defining the manifest.json file
To construct the connection form, it's important to include the necessary options in the manifest.json file. Here's an example of how to do it:
```json
"properties": {
"credentials": {
"label": "Authentication",
"key": "auth_type",
"type": "dropdown-component-flip",
"description": "A single select dropdown to choose credentials",
"list": [
{
"value": "personal_access_token",
"name": "Use Personal Access Token"
}
]
},
"personal_access_token": {
"token": {
"label": "Token",
"key": "personal_token",
"type": "password",
"description": "Enter your personal access token",
"hint": "You can generate a personal access token from your Github account settings."
}
}
}
```
This manifest.json file includes information about authentication options, specifically a dropdown to choose a type of credentials and a field to enter a personal access token. The label, key, type, description, and hint properties are used to define the specific fields and their types required for connecting to the API or data source.
In this particular code, there are two main properties defined: **`credentials`** and **`personal_access_token`**.
The **`credentials`** property specifies the authentication method to be used. It contains several keys:
- **`label`**: a user-friendly label for the authentication method, set to "Authentication"
- **`key`**: a unique identifier for the authentication method, set to "auth_type"
- **`type`**: the type of the authentication method, set to "dropdown-component-flip"
- **`description`**: a description of the authentication method, set to "A single select dropdown to choose credentials"
- **`list`**: an array of objects representing the different authentication methods available. In this case, there is only one method available: a personal access token. The `value` key in the object is set to "personal_access_token" and the `name` key is set to "Use Personal Access Token".
The **`personal_access_token`** property specifies the details of the personal access token authentication method. It contains a `token` key, which specifies the actual personal access token to be used. The `token` key contains several keys:
- **`label`**: a user-friendly label for the personal access token, set to "Token"
- **`key`**: a unique identifier for the personal access token, set to "personal_token"
- **`type`**: the type of the personal access token, set to "password"
- **`description`**: a description of the personal access token, set to "Enter your personal access token"
- **`hint`**: a hint for the personal access token, set to "You can generate a personal access token from your Github account settings."
The available `type` options are:
However, based on the code you provided, the available **`type`** options are:
- **`password`**: used to input a secret value, such as a password or an access token.
- **`dropdown-component-flip`**: used to create a dropdown menu that flips its position relative to the component that triggers it.
- **`text`**: used to input a single line of text.
- **`textarea`**: used to input multiple lines of text.
- **`toggle`**: used to create a simple on/off switch.
- **`react-component-headers`**: used to display headers for React components.
- **`codehinter`**: is a specialized input field used for entering code and has additional functionality, such as resolving JavaScript code within double curly braces`{{}}`.
:::tip
The **manifest.json** file is utilized by the connection modal component, which appears to prompt users to enter their datasource credentials. Meanwhile, the **operations.json** file is used by the query manager when users generate a specific query for a connected datasource. **Both files utilize a similar schema**.
:::
## Step 3: Defining the operations.json file
```json
"properties": {
"operation": {
"label": "Operation",
"key": "operation",
"type": "dropdown-component-flip",
"description": "Single select dropdown for operation",
"list": [
{
"value": "get_user_info",
"name": "Get user info"
},
{
"value": "get_repo",
"name": "Get repository"
},
{
"value": "get_repo_issues",
"name": "Get repository issues"
},
{
"value": "get_repo_pull_requests",
"name": "Get repository pull requests"
}
]
},
"get_user_info": {
"username": {
"label": "Username",
"key": "username",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter username",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "Enter username"
}
},
"get_repo": {
"owner": {
"label": "Owner",
"key": "owner",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter owner name",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "developer"
},
"repo": {
"label": "Repository",
"key": "repo",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter repository name",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "tooljet"
}
},
"get_repo_issues": {
"owner": {
"label": "Owner",
"key": "owner",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter owner name",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "developer"
},
"repo": {
"label": "Repository",
"key": "repo",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter repository name",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "tooljet"
},
"state": {
"label": "State",
"key": "state",
"className": "codehinter-plugins col-4",
"type": "dropdown",
"description": "Single select dropdown for choosing state",
"list": [
{
"value": "open",
"name": "Open"
},
{
"value": "closed",
"name": "Closed"
},
{
"value": "all",
"name": "All"
}
]
}
},
"get_repo_pull_requests": {
"owner": {
"label": "Owner",
"key": "owner",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter owner name",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "developer"
},
"repo": {
"label": "Repository",
"key": "repo",
"type": "codehinter",
"lineNumbers": false,
"description": "Enter repository name",
"width": "320px",
"height": "36px",
"className": "codehinter-plugins",
"placeholder": "tooljet"
},
"state": {
"label": "State",
"key": "state",
"type": "dropdown",
"className": "codehinter-plugins col-4",
"description": "Single select dropdown for choosing state",
"list": [
{
"value": "open",
"name": "Open"
},
{
"value": "closed",
"name": "Closed"
},
{
"value": "all",
"name": "All"
}
]
}
}
}
```
The operations.json file specifies the available operations that can be executed on the data source. It provides details about the operation type, required fields to execute the operation, and the data type of each field. The label, key, type, description, and hint properties are used to define the specific fields and their types required to establish a connection with the API or data source.
## Step 4: Add the npm package of GitHub to the plugin dependencies
- Change directory to the plugin directory where the npm package needs to be installed and then install the package
```bash
# change directory to the plugin directory and install the npm package
npm i octokit --workspace=@tooljet-marketplace/github
```
:::info
Steps to install npm package to a plugin
```bash
npm i <npm-package-name> --workspace=<plugin-name-in-package-json>
```
The command `npm i <npm-package-name> --workspace=<plugin-name-in-package-json>` is used to install a specific npm package into a particular workspace of a multi-package repository.
The *--workspace* flag is used to specify the workspace where the package should be installed. In this case, we are installing the package in the *@tooljet-marketplace/github* workspace.
:::
## Step 5: Implement the query execution logic in index.ts
In index.ts, the query execution logic needs to be implemented for the Github plugin's QueryService. The QueryService is responsible for handling the process of running queries and receives information about the data source, including credentials, configurations, and query parameters.
For the Github data source, the sourceOptions will contain the necessary authentication credentials, like the personal access token, while the queryOptions will include the configurations and parameters specific to the query, like obtaining a list of repositories for a particular user.
Using this information, the QueryService will create and execute API requests against the Github API. The resulting data will be returned to the caller for further processing as needed.
Create a new file **query_operations.ts** in the **plugins/github/src** directory and add the following code to it.
```typescript
import { Octokit } from 'octokit'
import { QueryOptions } from './types'
export async function getUserInfo(octokit: Octokit, options: QueryOptions): Promise<object> {
const { data } = await octokit.request(
'GET /users/{username}',
{
username: options.username
}
);
return data;
}
export async function getRepo(octokit: Octokit, options: QueryOptions): Promise<object> {
const { data } = await octokit.request(
'GET /repos/{owner}/{repo}',
{
owner: options.owner,
repo: options.repo
}
);
return data;
}
export async function getRepoIssues(octokit: Octokit, options: QueryOptions): Promise<object> {
const { data } = await octokit.request(
'GET /repos/{owner}/{repo}/issues',
{
owner: options.owner,
repo: options.repo,
state: options.state || 'all'
}
);
return data;
}
export async function getRepoPullRequests(octokit: Octokit, options: QueryOptions): Promise<object> {
const { data } = await octokit.request(
'GET /repos/{owner}/{repo}/pulls',
{
owner: options.owner,
repo: options.repo,
state: options.state || 'all'
}
);
return data;
}
```
The query_operations.ts file comprises functions that will execute the queries and will be called by the QueryService in index.ts.
The GitHub class has three methods:
- **run**: This method executes a query and is invoked by passing sourceOptions and queryOptions as input, representing the source metadata and query configuration, respectively. The run method utilizes the octokit library to send API requests to the GitHub API and returns the query result in a QueryResult object.
- **testConnection**: When adding a new data source to a ToolJet application, the connection can be tested. The testConnection method is used to test the connection, and it takes in sourceOptions as input, which represents the source metadata. The method tests the connection by trying to fetch the authenticated user and returns a ConnectionTestResult object indicating whether the connection was successful.
:::note
Not all data sources may support testing connections. If it's not applicable for your data source, you can disable the test connection feature by adding "customTesting": true to your plugin's manifest.json.
:::
- **getConnection**: This method is a helper function that returns an authenticated octokit client, which is utilized to send requests to the GitHub API. It takes in sourceOptions as input, representing the source metadata, and returns an authenticated octokit client.
## Step 6: Add Error Handling
In case of an error, it is necessary to return the error message received from the Plugin SDK. To achieve this, include the `errorDetails` in the **run** method within the **index.ts** file. The specific parameters of the error may vary depending on the plugin. <br/><br/>
Additionally, the **data** field in the Plugin SDK corresponds to **errorDetails** in the code, and the dynamically generated **errorMessage** maps to the **description** field in the error preview.
#### Example
Consider the case of MongoDB. If an error occurs, such as the following:
<img className="screenshot-full" src="/img/contributing-guide/create-plugin/mongodb-error.png" alt="MongoDB Error" />
You can implement error handling using the following code:
```js
catch (error) {
let errorMessage = 'An unknown error occurred';
let errorDetails = {};
if (error instanceof Error) {
errorMessage = error.message || errorMessage;
errorDetails = {
name: error.name,
code: (error as any).code || null,
codeName: (error as any).codeName || null,
keyPattern: (error as any).keyPattern || null,
keyValue: (error as any).keyValue || null,
};
}
throw new QueryError('Query could not be completed', errorMessage, errorDetails);
}
```
This code ensures that error messages and details are properly returned to the Plugin SDK, enabling meaningful error previews for the user.
<img className="screenshot-full" src="/img/contributing-guide/create-plugin/query-error.png" alt="Query Error" />
## Delete a plugin
To delete a plugin, enter the following command:
```bash
tooljet plugin delete PLUGIN_NAME
```
The CLI will prompt users to verify if the plugin to be deleted is a marketplace plugin before proceeding with the deletion.
## Publish a plugin
To release a plugin, submit a pull request on ToolJet's GitHub Repository after creating it. The ToolJet team will review the pull request, and if approved, the plugin will be included and published in the next release.

View file

@ -0,0 +1,80 @@
---
id: marketplace-setup
title: 'Marketplace: Development Setup'
---
The Marketplace offers custom plugins that can be installed in your ToolJet instance. This guide aims to assist you in creating a new plugin for the ToolJet marketplace.
## Requirements
- [Node.js](https://nodejs.org/en/download/) **(v18.18.2)**
- [npm](https://www.npmjs.com/get-npm) **(v9.8.1)**
## Getting Started
### Step 1. Setup ToolJet Locally
To obtain the ToolJet repository via git, use the command:
```bash
git clone https://github.com/ToolJet/ToolJet.git
```
Next, refer to the appropriate guide for your development environment to follow the Setup instructions:
- **[MacOS](/docs/contributing-guide/setup/macos)**
- **[Docker](/docs/contributing-guide/setup/docker)**
- **[Ubuntu](/docs/contributing-guide/setup/ubuntu)**
### Step 2. Enabling the Marketplace for your Instance
To enable the marketplace for your ToolJet instance, you need to specify the following environment variables in your **`.env`** file:
#### Marketplace Feature Enable
Use this environment variable to enable/disable the feature that allows users to use the marketplace.
| variable | value |
| -------------------------- | ----------------- |
| ENABLE_MARKETPLACE_FEATURE | `true` or `false` |
#### Enable Marketplace Plugin Developement Mode
The use of this environment variable facilitates plugin development by enabling automatic builds whenever package changes occur, thus simplifying the development process. Moreover, it also incorporates a reload button that retrieves all the recent local modifications from the file system for installed plugins, making it a valuable feature for improving the overall development experience.
| variable | value |
| -------------------------- | ----------------- |
| ENABLE_MARKETPLACE_DEV_MODE | `true` or `false` |
Please note that the marketplace is not enabled by default. After updating the variable, restart your ToolJet instance.
For information on running ToolJet on your local machine, please refer to the instructions provided **[here](/docs/contributing-guide/setup/architecture)**. You can access the marketplace by navigating to the **'/integrations'** route.
### Step 3: Install the Required Packages
The required packages must be installed from the marketplace root folder. Use the following commands to install the packages:
``` bash
cd marketplace
npm install
```
After the packages are installed, run the following command to build the directory:
``` bash
npm run build
```
### Step 4: Installation of tooljet-cli
In order to manage plugins for the ToolJet marketplace, including creating, updating, and deleting, you will need to utilize **[tooljet-cli](https://www.npmjs.com/package/@tooljet/cli)**. This can be installed via npm by entering the following command:
```bash
npm install -g @tooljet/cli
# Ensure the installation was successful
tooljet --version
```
Having completed the environment setup for Marketplace Developer mode, we can proceed to the next section and commence with [developing the first plugin](/docs/contributing-guide/marketplace/creating-a-plugin).

View file

@ -0,0 +1,5 @@
{
"label": "Setup",
"position": 1,
"collapsed": true
}

View file

@ -0,0 +1,25 @@
---
id: architecture
title: Architecture
---
# Introduction
ToolJet has two main components: **ToolJet Server** and **ToolJet Client**.
### 1. ToolJet Server
ToolJet server is a Node.js API application. Server is responsible for authentication, authorization, persisting application definitions, running queries, storing data source credentials securely and more.
**Dependencies:**
- **PostgreSQL** - ToolJet server persists data to a postgres database.
- **Email service** (SMTP/Sendgrid/Mailgun/etc) - Required to send user invitations and password reset emails.
- **PostgREST** - Standalone web server that converts PostgreSQL database into queryable RESTful APIs for ToolJet Database.
### 2. ToolJet Client
ToolJet client is a ReactJS application. Client is responsible for visually editing the applications, building & editing queries, rendering applications, executing events and their trigger, etc.
## Requirements
1. **Node version 18.18.2**
2. **npm version 9.8.1**

View file

@ -0,0 +1,117 @@
---
id: codespaces
title: GitHub Codespaces
---
Follow the steps below to set up ToolJet on GitHub Codespaces. We recommend reading our guide on [architecture](https://docs.tooljet.ai/docs/contributing-guide/setup/architecture) of ToolJet before proceeding.
Open the terminal and run the commands below.
## Setting up
### 1. Set up the environment
1. Install Node.js ( version: v18.18.2 ) and npm (version: v9.8.1)
```
nvm install 18.18.2
nvm use 18.18.2
npm install -g npm@9.8.1
```
2. Install Postgres
```
sudo sh -c 'echo "deb [http://apt.postgresql.org/pub/repos/apt](http://apt.postgresql.org/pub/repos/apt) $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - [https://www.postgresql.org/media/keys/ACCC4CF8.asc](https://www.postgresql.org/media/keys/ACCC4CF8.asc) | sudo apt-key add -
sudo apt-get update
sudo apt-get install postgresql-13 postgresql-contrib-13
```
To start the postgresql service run the below command:
```
sudo service postgresql start
```
If you wish to change the password of the installed postresql service run the below commands:
```
sudo su
sudo -u postgres psql
\password postgres
\q
```
### 2. Set up environment variables
Create a `.env` file by running the command `touch .env`. More information on the variables that can be set is given in the [environment variables reference](https://docs.tooljet.ai/docs/setup/env-vars)
**For basic set-up you add the below env variables:**
```
TOOLJET_HOST=http://localhost:3000
LOCKBOX_MASTER_KEY=
SECRET_KEY_BASE=
PG_USER=postgres
PG_HOST=localhost
PG_PASS=postgres
PG_DB=tooljet_prod
SUB_PATH=/apps/tooljet/
NODE_ENV=production
SERVE_CLIENT=true
```
> `SECRET_KEY_BASE` requires a 64 byte key. (If you have `openssl` installed, run `openssl rand -hex 64` to create a 64 byte secure random key)
>
> `LOCKBOX_MASTER_KEY` requires a 32 byte key. (Run `openssl rand -hex 32` to create a 32 byte secure random key)
### 3. Install and build dependencies
Make sure node version is set to 18.18.2 before running the below command:
```
npm install
npm install --prefix server
npm install --prefix frontend
npm run build:plugins
```
### 4. Set up database
```
npm run --prefix server db:create
npm run --prefix server db:migrate
```
If at any point you need to reset the database, use this command `npm run --prefix server db:reset`
### 5. Build client
```
cd ./frontend && NODE=production npm run build
```
### 6. Run server
```
cd ./server && npm run start:prod
```
The client will start on the **port 3000**, you can access the client by visiting the url created by codespace - `https://<url>/apps/tooljet`

View file

@ -0,0 +1,189 @@
---
id: docker
title: Docker
---
:::warning
The following guide is intended for contributors to set up ToolJet locally. If you're interested in **self-hosting** ToolJet, please refer to the **[Setup](/docs/setup/)** section.
:::
Docker Compose is the easiest way to set up the ToolJet server and client locally.
_If you just want to try out ToolJet locally with docker, you can follow the steps [here](/docs/setup/try-tooljet)._
## Prerequisites
Make sure you have the latest version of `docker` and `docker compose` installed.
**[Official docker installation guide](https://docs.docker.com/desktop/)**
**[Official docker-compose installation guide](https://docs.docker.com/compose/install/)**
## Setting up
:::warning
If you are setting up on a Windows machine, we advise you to set up Docker Desktop with WSL2. More information is available [here](https://docs.docker.com/desktop/windows/wsl/).
Make sure to run it within the WSL2 terminal.
:::
1. Fork the repository:
Go to the [ToolJet GitHub repository](https://github.com/ToolJet/Tooljet), click on the **Fork** button to create a copy of the repository under your own GitHub account.
2. Clone your forked repository:
After forking, clone the forked repository to your local machine using the URL of your forked repo.
```bash
git clone https://github.com/<your-username>/ToolJet.git
```
3. Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the **[environment variables reference](/docs/setup/env-vars)**.
```bash
cp ./deploy/docker/.env.internal.example .env
```
4. Populate the keys in the `.env` using the below the command:
```bash
chmod +x ./deploy/docker/internal.sh && ./deploy/docker/internal.sh
```
:::warning
If you are setting up on a Windows machine, please ensure that the .env file line endings are set to LF, as they will be CRLF by default unless configured otherwise.
:::
5. Build Docker images.
```bash
docker compose build
docker compose run --rm plugins npm run build:plugins
```
6. Run ToolJet.
```bash
docker compose up
```
ToolJet should now be served locally at `http://localhost:8082`.
7. To shut down the containers, use the below commands:
```bash
docker compose stop
```
## Making changes to the codebase
If you make any changes to the codebase or pull the latest changes from upstream, the ToolJet server container will hot reload the application without any action required from you.
**Note:**
1. If the changes include database migrations or new npm package additions in `package.json`, you need to restart the ToolJet server container by running `docker compose restart server`.
2. If you need to add a new binary or system library to the container itself, you would need to add those dependencies in `docker/server.Dockerfile.dev` and then rebuild the ToolJet server image. You can do that by running `docker compose build server`. After the build completes, you can start all services by running `docker compose up`.
Example:
Let's say you need to install the `imagemagick` binary in your ToolJet server's container. You'd then need to make sure that `apt` installs `imagemagick` while building the image. The Dockerfile at `docker/server.Dockerfile.dev` for the server would then look something like this:
```bash
FROM node:18.18.2-buster AS builder
RUN apt update && apt install -y \
build-essential \
postgresql \
freetds-dev \
imagemagick
RUN mkdir -p /app
WORKDIR /app
COPY ./server/package.json ./server/package-lock.json ./
RUN npm install
ENV NODE_ENV=development
COPY ./server/ ./
COPY ./docker/ ./docker/
COPY ./.env ../.env
RUN ["chmod", "755", "entrypoint.sh"]
```
Once you've updated the Dockerfile, rebuild the image by running `docker compose build server`. After building the new image, start the services by running `docker compose up`.
## Running Tests
Test config picks up config from `.env.test` file at the root of the project.
1. Run the following command to create and migrate data for test db:
```bash
docker compose run --rm -e NODE_ENV=test server npm run db:create
docker compose run --rm -e NODE_ENV=test server npm run db:migrate
```
2. To run the unit tests:
```bash
docker compose run --rm server npm run --prefix server test
```
3. To run e2e tests:
```bash
docker compose run --rm server npm run --prefix server test:e2e
```
4. To run a specific unit test:
```bash
docker compose run --rm server npm --prefix server run test <path-to-file>
```
## Troubleshooting
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our [Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-2rk4w42t0-ZV_KJcWU9VL1BBEjnSHLCA) if you encounter any issues when trying to run ToolJet locally.
## Debugging with Docker
In this section, we provide guidance on how to enable debugging for ToolJet services using Docker and Visual Studio Code. These additions will significantly benefit contributors by streamlining the debugging process and enhancing the overall development experience.
#### VSCode Launch Configuration:
A new configuration has been added in `.vscode/launch.json` to facilitate launching the client and server in debug mode. This allows contributors to easily debug the application within the Visual Studio Code environment. Configurations include:
- **Docker Debug Client**: Launch the client running in a Docker container for debugging within Visual Studio Code.
- **Docker Debug Server**: Debug the server in a Docker container, allowing developers to leverage Node.js debugging tools directly from their IDE.
#### VSCode Task Configuration:
A new task has been introduced in `.vscode/tasks.json` to manage Docker Compose commands for debugging. This includes tasks to start the client and server in detached mode, making it easier to initiate debugging sessions.
#### Docker Compose Debug Configuration:
The `docker-compose-debug.yaml` file defines the services for debugging, exposing the necessary port (9229) for Node.js debugging. This setup ensures that the server runs in debug mode, allowing for effective troubleshooting.
### Benefits of Debugging Configuration
These changes streamline the debugging process, making it more efficient for contributors to identify and fix issues. The integration with Visual Studio Code allows for advanced debugging features such as breakpoints and real-time variable inspection. Furthermore, standardizing the debugging setup fosters better collaboration among team members, facilitating knowledge sharing and improving the overall development workflow.
By implementing these configurations, ToolJet aims to enhance the development experience, enabling contributors to resolve issues swiftly and maintain project momentum.
If you want to run docker in debug mode use this command
```bash
docker-compose -f docker-compose.yaml -f docker-compose-debug.yaml up --build
```
**Open the Project in VSCode**: Open the ToolJet directory in Visual Studio Code.
Check Launch Configurations:
- Open the debug view by clicking on the Debug icon in the Activity Bar on the side of the window.
- Select the appropriate configuration, such as Docker Debug Client or Docker Debug Server.

View file

@ -0,0 +1,150 @@
---
id: macos
title: Mac OS
---
The following guide is intended for contributors to set-up ToolJet locally. If you're interested in **self-hosting** ToolJet, please refer to the **[Setup](/docs/setup/)** section.
To set up and run ToolJet on macOS for development, begin by opening your terminal and executing the commands listed below. For a better understanding of ToolJet's framework, we advise reviewing our [architecture guide](/docs/contributing-guide/setup/architecture) before proceeding.
## Setting up
1. Set up the environment
1.1 Install Homebrew
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```
1.2 Install Node.js ( version: v18.18.2 ) and npm (version: v9.8.1)
```bash
brew install nvm
export NVM_DIR=~/.nvm
source $(brew --prefix nvm)/nvm.sh
nvm install 18.18.2
nvm use 18.18.2
npm install -g npm@9.8.1
```
1.3 Install Postgres
:::tip
ToolJet uses a postgres database as the persistent storage for storing data related to users and apps. We do not plan to support other databases such as MySQL.
:::
```bash
brew install postgresql@13
```
1.4 Install PostgREST
:::info
Please use PostgREST version 12.2.0
:::
```bash
brew install postgrest
```
1.5 Fork the repository:
Go to the [ToolJet GitHub repository](https://github.com/ToolJet/Tooljet), click on the **Fork** button to create a copy of the repository under your own GitHub account.
1.6 Clone your forked repository:
After forking, clone the forked repository to your local machine using the URL of your forked repo.
```bash
git clone https://github.com/<your-username>/ToolJet.git
```
2. Set up environment variables
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/setup/env-vars)
```bash
cp .env.example .env
```
3. Populate the keys in the env file
:::info
`SECRET_KEY_BASE` requires a 64 byte key. (If you have `openssl` installed, run `openssl rand -hex 64` to create a 64 byte secure random key)
`LOCKBOX_MASTER_KEY` requires a 32 byte key. (Run `openssl rand -hex 32` to create a 32 byte secure random key)
:::
Example:
```bash
cat .env
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY=1d291a926ddfd221205a23adb4cc1db66cb9fcaf28d97c8c1950e3538e3b9281
SECRET_KEY_BASE=4229d5774cfe7f60e75d6b3bf3a1dbb054a696b6d21b6d5de7b73291899797a222265e12c0a8e8d844f83ebacdf9a67ec42584edf1c2b23e1e7813f8a3339041
NODE_ENV=development
# DATABASE CONFIG
PG_HOST=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASS=postgres
PG_DB=tooljet_development
TOOLJET_DB=tooljet_db
TOOLJET_DB_USER=postgres
TOOLJET_DB_HOST=localhost
TOOLJET_DB_PASS=postgres
ORM_LOGGING=all
```
4. Install and build dependencies
```bash
npm install
npm install --prefix server
npm install --prefix frontend
npm run build:plugins
```
5. Set up database
```bash
npm run --prefix server db:create
npm run --prefix server db:reset
```
:::info
If at any point you need to reset the database, use this command `npm run --prefix server db:reset`
:::
6. Run plugins compilation in watch mode
```bash
cd ./plugins && npm start
```
7. Run the server
```bash
cd ./server && npm run start:dev
```
8. Run the client
```bash
cd ./frontend && npm start
```
The client will start on the port 8082, you can access the client by visiting: [http://localhost:8082](http://localhost:8082)
9. Create login credentials
Visiting [http://localhost:8082](http://localhost:8082) should redirect you to the login page, click on the signup link and enter your email. The emails sent by the server in development environment are captured and are opened in your default browser. Click the invitation link in the email preview to setup the account.
## Running tests
Test config requires the presence of `.env.test` file at the root of the project.
To run the unit tests
```bash
npm run --prefix server test
```
To run e2e tests
```bash
npm run --prefix server test:e2e
```
To run a specific unit test
```bash
npm run --prefix server test <path-to-file>
```

View file

@ -0,0 +1,24 @@
---
id: system-requirements-for-contributing
title: System Requirements
---
This page details the system requirements for setting up and running ToolJet on both Docker local and Bare-Metal setups.
## Docker
**Windows:**
When configuring Docker Desktop on your Windows machine, ensure you have a minimum of 16GB RAM for optimal performance.
**Mac:**
For Docker setups on Mac systems, it's advisable to use a machine with 16GB RAM to enhance operational efficiency.
## Bare-Metal Setup
For those who prefer a [bare-metal](/docs/contributing-guide/setup/docker) setup over Docker, a minimum of 8GB RAM is recommended for smooth operation.
Before initiating the installation process, please verify that your system meets these specified requirements. It's essential to customize server specifications based on the unique demands of your deployment scenario.

View file

@ -0,0 +1,164 @@
---
id: ubuntu
title: Ubuntu
---
:::warning
The following guide is intended for contributors to set-up ToolJet locally. If you're interested in **self-hosting** ToolJet, please refer to the **[Setup](/docs/setup/)** section.
:::
Follow these steps to setup and run ToolJet on Ubuntu. Open terminal and run the commands below.
## Setting up
1. Set up the environment
1.1 Install NVM
```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
```
Use the command to load NVM:
```bash
source ~/.nvm/nvm.sh
```
Close and reopen your terminal to start using nvm
```bash
nvm install 18.18.2
```
Ensure you have the correct version of npm, or it will cause an error about fsevents.
```bash
npm i -g npm@9.8.1
```
1.2 Install Postgres
```bash
sudo apt install postgresql postgresql-contrib
sudo apt-get install libpq-dev
```
1.3 Install PostgREST
:::info
Please use PostgREST version 12.2.0
:::
Please follow the installation [PostgREST](https://postgrest.org/en/stable/install.html) guide
2. Setup the repository:
2.1 Fork the repository:
Go to the [ToolJet GitHub repository](https://github.com/ToolJet/Tooljet), click on the **Fork** button to create a copy of the repository under your own GitHub account.
2.2 Clone your forked repository:
After forking, clone the forked repository to your local machine using the URL of your forked repo.
```bash
git clone https://github.com/<your-username>/ToolJet.git
```
3. Set up environment variables
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/setup/env-vars)
```bash
cp .env.example .env
```
4. Populate the keys in the env file
:::info
`SECRET_KEY_BASE` requires a 64 byte key. (If you have `openssl` installed, run `openssl rand -hex 64` to create a 64 byte secure random key)
`LOCKBOX_MASTER_KEY` requires a 32 byte key. (Run `openssl rand -hex 32` to create a 32 byte secure random key)
:::
ToolJet requires the following environment variables to be set.
```envs
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY= <generate using 'openssl rand -hex 32'>
SECRET_KEY_BASE= <generate using 'openssl rand -hex 64'>
NODE_ENV=development
PG_HOST=localhost
PG_PORT=5432
PG_USER=postgres
PG_PASS=postgres
PG_DB=tooljet_development
TOOLJET_DB=tooljet_db
TOOLJET_DB_USER=postgres
TOOLJET_DB_HOST=localhost
TOOLJET_DB_PASS=postgres
```
ToolJet requires two separate databases to function correctly `pg_db` and `tooljet_db`,
While both databases can reside on the same PostgreSQL host, they must be separate databases to avoid conflicts.
5. Install and build dependencies
```bash
npm install
npm install --prefix server
npm install --prefix frontend
npm run build:plugins
```
:::note
If the `npm run build:plugins` command fails due to some packages are missing, try running the following command to install the necessary packages:
`sudo apt install build-essential`
then proceed to `npm run build:plugins` step again.
:::
6. Set up database
```bash
npm run --prefix server db:create
npm run --prefix server db:reset
```
:::info
If at any point you need to reset the database, use this command `npm run --prefix server db:reset`
:::
7. Run plugins compilation in watch mode
```bash
cd ./plugins && npm start
```
8. Run the server
```bash
cd ./server && npm run start:dev
```
9. Run the client
```bash
cd ./frontend && npm start
```
The client will start running on the port 8082, you can access the client by visiting: [http://localhost:8082](http://localhost:8082)
10. Create login credentials
Visiting https://localhost:8082 should redirect you to the login page, click on the signup link and enter your email. The emails sent by the server in development environment are captured and are opened in your default browser. Click the invitation link in the email preview to setup the account.
## Running tests
Test config requires the presence of `.env.test` file at the root of the project.
To run the unit tests
```bash
npm run --prefix server test
```
To run e2e tests
```bash
npm run --prefix server test:e2e
```
To run a specific unit test
```bash
npm run --prefix server test <path-to-file>
```

View file

@ -0,0 +1,18 @@
---
id: windows
title: Windows
---
To run ToolJet, please install it in an Ubuntu environment using **[Windows Subsystem for Linux 2](https://learn.microsoft.com/en-us/windows/wsl/install-manual#step-2---check-requirements-for-running-wsl-2)**. You can obtain the Ubuntu environment from the **Microsoft Store** by visiting this [link](https://apps.microsoft.com/store/detail/ubuntu-22042-lts/9PN20MSR04DW).
After successfully installing the Ubuntu environment, you will have access to a terminal window similar to the one shown below:
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/contributing-guide/windows/wsl2.png" alt="Windows setup" />
</div>
:::warning
If you are setting up ToolJet on a Windows machine, ensure that the line endings in the **.env** file are changed to LF. By default, they may be set to CRLF, which is not compatible unless configured specifically for Windows machines.
:::
Once the environment is set up, you can proceed with the steps outlined in the Ubuntu documentation at **[Contributing Guide - Ubuntu Setup](/docs/contributing-guide/setup/ubuntu)**.

View file

@ -0,0 +1,90 @@
---
id: slackcoc
title: Slack Code of Conduct
---
# Slack Code of Conduct
This code of conduct governs ToolJet's Slack Community events and discussions.
---
## Introduction
- Diversity and inclusion make our community strong. We encourage participation from the most varied and diverse backgrounds possible and want to be very clear about where we stand.
- Our goal is to maintain a safe, helpful and friendly community for everyone, regardless of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other defining characteristic.
- This code and related procedures apply to unacceptable behavior occurring in all community venues, including behavior outside the scope of community activities — online and in-person— as well as in all one-on-one communications, and anywhere such behavior has the potential to adversely affect the safety and well-being of community members.
## Expected behavior
- Be welcoming.
- Be kind.
- Look out for each other.
## Unacceptable Behavior
- Conduct or speech which might be considered sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory or offensive in nature.
- Do not use unwelcome, suggestive, derogatory or inappropriate nicknames or terms.
- Do not show disrespect towards others. (Jokes, innuendo, dismissive attitudes.)
- Intimidation or harassment (online or in-person).
- Disrespect towards differences of opinion.
- Inappropriate attention or contact. Be aware of how your actions affect others. If it makes someone uncomfortable, stop.
- Not understanding the differences between constructive criticism and disparagement.
- Sustained disruptions.
- Violence, threats of violence or violent language.
## Where does the Code of Conduct apply?
This Code of Conduct applies to all spaces managed by ToolJet. This includes:
- Conferences (including social events and peripheral activities)
- Unconferences and sprints
- Meetups, including their discussion boards
- Workshops
- Presentation materials used in talks or sessions
- Slack
- GitHub
- Twitter hashtag and mentions
- Any forums created by the ToolJet which the community uses for communication.
The Code of Conduct does not exclusively apply to slack or events on an official agenda. For example, if after a scheduled social event you go to a bar with a group of fellow participants, and someone harasses you there, we would still treat that as a CoC violation. Similarly, harassment in Twitter direct messages related to ToolJet can still be covered under this Code of Conduct.
In addition, violations of this code outside our spaces may affect a persons ability to participate in them.
## Enforcement
- Understand that speech and actions have consequences, and unacceptable behavior will not be tolerated.
- If you are the subject of, or witness to any violations of this Code of Conduct, please contact us via email at hello@tooljet.com or dm @navaneeth on slack.
- If violations occur, organizers will take any action they deem appropriate for the infraction, up to and including expulsion.
:::info
Portions derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/), [The Rust Code of Conduct](https://www.rust-lang.org/conduct.html) and [The Ada Initiative](https://adainitiative.org) under a Creative Commons Attribution-ShareAlike license.
:::
---
## Etiquettes to follow
#### 1. Be nice to everyone
#### 2. Check off your resolved questions
If you have received a useful reply to your question, please drop a ✅ reaction or a reply for affirmation.
#### 3. Try not to repost question
If you have asked a question and have not got a response in 24hrs, please review your question for clarity and revise it. If you still feel you haven't received adequate response, feel free to ping @navaneeth.
#### 4. Post in public
Please don't direct message any individual member of ToolJet community without their explicit permission, independent of reason. Your question might be helpful for other community members.
#### 5. Don't spam tags
ToolJet's community of volunteer is very active and helpful, generally avoid tagging members unless it is urgent.
#### 6. Use threads for discussion
To keep the main channel area clear, we request to use threads to keep an ongoing conversation organized.

View file

@ -0,0 +1,62 @@
---
id: testing
title: Testing
---
Follow the steps below to setup and run the test specifications using Cypress. We recommend [setting up ToolJet locally](/docs/contributing-guide/setup/macos) before proceeding.
## Setting up
- Navigate to the `cypress-tests` directory and enter the following command:
```bash
npm install
```
## Running Tests
#### Headed mode
- To run cypress in **headed** mode, run the following command:
```bash
npm run cy:open
```
- In **headed** mode, the user will be able to choose the test specs from the test runner:
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/testing/headed.png" alt="Cypress headed mode" />
</div>
#### Headless mode
- To run cypress in **headless** mode, run the following command:
```bash
npm run cy:run
```
- To run a specific spec in headless mode, run the following command:
```bash
npm run cy:run -- --spec "cypress/e2e/dashboard/multi-workspace/manageSSO.cy.js
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/testing/headless.png" alt="Cypress headless mode" />
</div>
:::caution
If some test specs need the environment variables, the user can pass them similar to the following command:
```bash
npm run cy:open -- --env='{"pg_host":"localhost","pg_user":"postgres", "pg_password":"postgres"}'
```
or the user can add env-vars in the **cypress.config.js** file
:::
:::info
Check all the Cypress commands [here](https://docs.cypress.io/guides/guides/command-line#Commands)
:::

View file

@ -0,0 +1,46 @@
---
id: eslint
title: EsLint
---
# ESLint
ESLint as a code quality tool is a tool that checks your code for errors and helps you to fix them and enforces a coding style.
## Setup
1. Install the [ESLint extension](https://eslint.org/docs/latest/user-guide/integrations) for your code editor.
2. Set your editor's default formatter to `ESLint`.
:::tip
For VSCode users, you can set the formatter to `ESLint` in the [**settings.json**](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson).
:::
3. Install the dependencies.
```bash
npm install
npm install --prefix server
npm install --prefix frontend
```
4. Run the linter.
```bash
npm run --prefix server lint
npm run --prefix frontend lint
```
5. Fix the ESlint errors and warnings.
```bash
npm run --prefix server format
npm run --prefix frontend format
```
## Requirements
1. **Node version 18.18.2**
2. **npm version 9.8.1**
:::tip
It is recommended to check the VSCode **Setting.json**(Press `ctrl/cmnd + P` and search `>Settings (JSON)`) file to ensure there are no overrides to the eslint config rules. Comment the following rules for eslint: **eslint.options: `{...}`**.
:::

View file

@ -0,0 +1,40 @@
---
id: runpy-limitations
title: RunPy limitations
---
### Limitation: Unable to Open External URLs with urlopen in RunPy
When using the `urlopen` function within a RunPy query, you may encounter an error when trying to open external URLs, such as `https://api.baserow.io`. This limitation is due to the underlying framework used by RunPy, Pyodide, which has certain constraints and may not support all features available in a standard Python environment.
### Solution: Using fetch function with JavaScript
To overcome this limitation, you can utilize the `fetch` function from JavaScript, as Pyodide supports interoperability between Python and JavaScript. Here's an example of how to make an HTTP request using the `fetch` function in a RunPy query:
```python
from js import fetch
import json
async def push_data(url, data):
response = await fetch(
url,
method='POST',
headers=[
["Authorization", "Token <my_token>"],
["Content-Type", "application/json"]
],
body=data
)
reply = await response.json()
return reply
url = "https://api.baserow.io/api/database/rows/table/.../?user_field_names=true"
reply = await push_data(url, json.dumps(<my_data>))
reply
```
In the example above, the `fetch` function is used to make an HTTP POST request to the specified URL. The `Authorization` header is included to provide the necessary authentication token, and the request body is passed as JSON data.
By using the `fetch` function and incorporating JavaScript interoperability, you can successfully make HTTP requests within a RunPy query in scenarios where `urlopen` may encounter limitations.
It's important to note that the solution provided here assumes you have the necessary authorization token and data to push to the Baserow table. Adjust the code accordingly to fit your specific requirements.

View file

@ -0,0 +1,5 @@
{
"label": "Tutorials",
"position": 2,
"collapsed": true
}

View file

@ -0,0 +1,27 @@
---
id: creating-widget
title: Creating Widgets
---
# Creating Widgets
These are some of the most useful properties and functions passed to the widget
### properties
The `properties` object will contain the configurable properties of a widget, initially obtained from its definition on `widgetConfig.js`.
The values inside `properties` change whenever the developer makes changes to the inspector panel of ToolJet editor.
### exposedVariables
The `exposedVariables` object will contain the values of all exposed variables as configured in `widgetConfig.js`.
### setExposedVariable('exposedVariableName', newValue)
This function allows you to update the value of an exposed variable to `newValue`.
### validate(value)
This function validates the `value` passed based on the validation settings configured on the inspector panel for the widget.
It returns an array `[isValid, validationError]`, which represents respectively, whether the `value` passed is valid,
and the error message if there is one.

View file

@ -0,0 +1,5 @@
{
"label": "Datasource Reference",
"position": 5,
"collapsed": true
}

View file

@ -0,0 +1,269 @@
---
id: airtable
title: Airtable
---
ToolJet can connect to your **Airtable** account to read and write data.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **Airtable** 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.
ToolJet requires the following to connect to your Airtable:
- **Personal Access Token**
You can generate the Personal Access Token by visiting **[Developer Hub from your Airtable profile](https://support.airtable.com/docs/creating-and-using-api-keys-and-access-tokens#understanding-personal-access-token-basic-actions)**.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', borderRadius:'5px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)' }} className="screenshot-full" src="/img/datasource-reference/airtable/airtableconnect-v2.gif" alt="Airtable Data Source Connection" />
</div>
</div>
:::info
Airtable API has a rate limit, and at the time of writing this documentation, the limit is five(5) requests per second per base. You can read more about rate limits here **[Airtable API](https://airtable.com/api)**.
:::
<div style={{paddingTop:'24px'}}>
## Querying Airtable
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Airtable** datasource added in previous step.
3. Select the desired operation from the dropdown and enter the required parameters.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/airtable/operations.png" alt="Airtable Data Source Operations" />
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
- **[List records](#list-records)**
- **[Retrieve record](#retrieve-record)**
- **[Create record](#create-record)**
- **[Update record](#update-record)**
- **[Delete record](#delete-record)**
</div>
### List Records
This operation retrieves a list of records from the specified table.
#### Required Parameters
- **Base ID**: The unique identifier of the Airtable base.
- **Table name**: The name or ID of the table to retrieve records from.
#### Optional Parameters*
- **Page size**: The number of records to return per page.
- **Offset**: Used for pagination to fetch the next set of records.
- **Filter by formula**: A formula to filter records.
- **Fields**: Specifies which fields to include in the response.
- **Timezone**: The timezone to use for date and time fields.
- **User locale**: The locale to use for formatting date and time fields.
- **Cell format**: Determines how cell values are returned. Possible values are:
- **json**: Returns cell values as JSON objects, depending on the field type.
- **string**: Returns cell values as strings.
- **View**: Specifies the view to retrieve records from.
- **Sort**: Defines the sorting order of records.
:::info
Timezone and User locale are mutually dependent. If you provide a timezone, you must also provide a user locale and vice versa. These properties are only applied when cell format is set to string. To correctly format date and time fields, make sure the coloumn type is set to Date or Date Time in Airtable.
:::
<img className="screenshot-full" src="/img/datasource-reference/airtable/list-records-v3.png" alt="Airtable List Records Query" />
<details id="tj-dropdown">
<summary>**Example Values**</summary>
```json
Base ID: appO4WnRU3eTWnrDB
Table name: tblAPbj6KMjS8pxhH // Can be Table name or Table ID
Page size: 100
Offset: itrU18e2y6ITuMs1n/recjR8UdOZKjZ7aK3
Fields: ["Date", "Email", "Usage (# Weeks)"]
Filter by formula: IF({Usage (# Weeks)} < 10, 1, 0) // Only records with Usage (# Weeks) less than 10
Timezone: America/Chicago
User locale: en-gb
Cell format: string // Cell format needs to be string for Timezone and User locale to work
View: All Responses
Sort: createdTime // Select direction: Ascending or Descending
```
</details>
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"records": [
{
"id": "recToGRP6bWUG6djd",
"createdTime": "2016-11-21T20:21:40.000Z",
"fields": {
"Usage (# Weeks)": "3",
"Email": "Edith Lindon",
"Date": "11-21-2016"
}
},
{
"id": "recnUVJ8wwZbdECLk",
"createdTime": "2016-11-21T20:21:40.000Z",
"fields": {
"Usage (# Weeks)": "3",
"Email": "Marcellus Wong",
"Date": "11-21-2016"
}
},
{
"id": "recStKhQYw4Fn2qpj",
"createdTime": "2016-11-21T20:21:40.000Z",
"fields": {
"Usage (# Weeks)": "2",
"Email": "Lorraine Ljuba",
"Date": "11-21-2016"
}
}
]
}
```
</details>
### Retrieve Record
This operation fetches a specific record from the specified table.
#### Required Parameters
- **Base ID**
- **Table name**
- **Record ID**
<img className="screenshot-full" src="/img/datasource-reference/airtable/retrieve-record-v2.png" alt="Airtable Retrieve Record Query" />
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"id": "recu9xMnUdr2n2cw8",
"fields": {
"Notes": "Discuss project timeline",
"Name": "Michael Scott"
},
"createdTime": "2021-05-12T14:30:33.000Z"
}
```
</details>
### Create Record
This operation creates a new record in the specified table.
#### Required Parameters
- **Base ID**
- **Table name**
- **Records**
<img className="screenshot-full" src="/img/datasource-reference/airtable/create-record-v2.png" alt="Airtable Create Record Query" />
#### Example
```json title="Records"
[{
"fields": {
"Name": "Katrina Petersons",
"Email": "katrina.petersions@example.com"
}
}]
```
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"records": [
{
"id": "recu6jhA7tzv4K66s",
"createdTime": "2024-06-11T06:01:44.000Z",
"fields": {
"Name": "Katrina Petersons",
"Email": "katrina.petersions@example.com",
"Date": "06-11-2024",
}
}
]
}
```
</details>
### Update record
Update a specific record by providing new data.
#### Required parameters:
- **Base ID**
- **Table name**
- **Record ID**
- **Body**
<img className="screenshot-full" src="/img/datasource-reference/airtable/update-record-v2.png" alt="Airtable Update Record Query" />
#### Example
```json
{
"Email": "katrina.petersions2@example.com"
}
```
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"records": [
{
"id": "recu6jhA7tzv4K66s",
"createdTime": "2024-06-11T07:01:44.000Z",
"fields": {
"Name": "Katrina Petersons",
"Email": "katrina.petersions2@example.com",
"Date": "06-11-2024",
}
}
]
}
```
</details>
### Delete record
This operation removes a record from the specified table.
#### Required parameters:
- **Base ID**
- **Table name**
- **Record ID**
<img className="screenshot-full" src="/img/datasource-reference/airtable/delete-record-v2.png" alt="Airtable Delete Record Query" />
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
deleted: true
id: "recIKsyZgqI4zoqS7"
}
```
</details>

View file

@ -0,0 +1,77 @@
---
id: amazonses
title: Amazon SES
---
ToolJet can connect to your Amazon SES account to send emails.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **Amazon SES** 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 through the ToolJet dashboard.
ToolJet requires the following to connect to Amazon SES:
- **Region**
- **Authentication**
- **Access key**
- **Secret key**
**Note:** It is recommended to create a new IAM user for the database so that you can control the access levels of ToolJet.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/amazonses/connection-v2.png" alt="Amazon SES" />
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Querying Amazon SES
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Amazon SES** datasource added in previous step.
3. Select **Email service** as operation from the dropdown and enter the required parameters.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/amazonses/operations.png" alt="Amazon SES" />
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operation
### Email service
#### Required parameters:
- **Send email to**
- **Send email from**
- **Subject**
- **Body**
#### Optional parameters:
- **CC Addresses**
- **BCC Addresses**
<img className="screenshot-full" src="/img/datasource-reference/amazonses/email-service.png" alt="Amazon SES" />
:::info
**Send mail to** - accepts an array/list of emails separated by comma.
For example:
`{{["dev@tooljet.io", "admin@tooljet.io"]}}`.
**Send mail from** - accepts a string.
For example: `admin@tooljet.io`
:::
:::tip
**Send a single email to multiple recipients** - The `Send mail to` field can contain an array of recipients, which will send a single email with all of the recipients in the field.
:::
</div>

View file

@ -0,0 +1,149 @@
---
id: appwrite
title: Appwrite
---
ToolJet can connect to appwrite database to read/write data.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the Appwrite 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.
ToolJet requires the following to connect to your Appwrite:
- **Host (API endpoint)**
- **Project ID**
- **Secret Key**
You'll find the Secret Key and other credentials on your Appwrite's project settings page. You may need to create a new key if you don't have one already.
:::info
You should also set the scope for access to a particular resource. Learn more about the **API keys and scopes** [here](https://appwrite.io/docs/keys).
:::
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/appwrite/connect-v3.png" alt="Appwrite intro"/>
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Querying Appwrite
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Appwrite** datasource added in previous step.
3. Select the operation you want to perform.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/appwrite/querying-v3.png" alt="Appwrite intro"/>
</div>
:::tip
Query results can be transformed using Transformations. Read our **Transformation Documentation** [here](/docs/beta/app-builder/custom-code/transform-data).
:::
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
- **[List Documents](#list-documents)**
- **[Get Document](#get-document)**
- **[Add Document to Collection](#add-document-to-collection)**
- **[Update Document](#update-document)**
- **[Delete Document](#delete-document)**
### List Documents
This operation is used to get a list of all the user documents.
#### Required Parameters
- **Collection ID**
#### Optional Parameters
- **Limit**
- **Order fields**
- **Order types**
- **Field**
- **Operator**
- **Value**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/appwrite/list-v3.png" alt="Appwrite List" />
</div>
### Get Document
Use this operation to get a document from a collection by its unique ID.
#### Required Parameters
- **Collection ID**
- **Document ID**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/appwrite/get-v3.png" alt="Appwrite get" />
</div>
### Add Document to Collection
Use this operation to create a new document in a collection.
#### Required Parameters
- **Collection ID**
- **Body**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/appwrite/add-v3.png" alt="Appwrite add" />
</div>
### Update Document
Use this operation to update a document.
#### Required Parameters
- **Collection ID**
- **Document ID**
- **Body**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/appwrite/upd-v3.png" alt="Appwrite update" />
</div>
### Delete Document
Use this operation for deleting a document in the collection.
#### Required Parameters
- **Collection ID**
- **Document ID**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/appwrite/del-v3.png" alt="Appwrite delete"/>
</div>
</div>

View file

@ -0,0 +1,103 @@
---
id: athena
title: Athena
---
ToolJet can connect to **Amazon Athena** which is an interactive query service that makes it easy to analyze data in Amazon S3 using standard SQL.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **Amazon Athena** 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 **Amazon Athena** as the data source.
ToolJet requires the following to connect to your Athena.
- **Database**
- **S3 output location**
- **Access key**
- **Secret key**
- **Region**
:::info
You can also configure for **[additional optional parameters](https://github.com/ghdna/athena-express)**.
:::
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/athena/athena-connection-v2.png" alt="Athena connection" />
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Querying Amazon Athena
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Amazon Athena** datasource added in previous step.
3. Select the SQL Query from the dropdown and enter the query.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
:::tip
**Refer amazon athena docs here for more info:** [link](https://docs.aws.amazon.com/athena/latest/ug/what-is.html)
:::
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/athena/querying-amazon-athena.png" alt="Athena connection" />
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Basic Queries
### Creating Table
This query is used to create an external table within the database. The data for this table is stored in an S3 bucket at the provided URL (`s3://athena-express-akiatfa53s-2022/` in this example).
```sql
CREATE EXTERNAL TABLE student (
name STRING,
age INT
) LOCATION 's3://athena-express-akiatfa53s-2022/';
```
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/athena/createTable.png" alt="Athena connection" />
### Inserting to Table
This query is attempting to insert a new record into the *student* table in a database.
```sql
INSERT INTO student
VALUES ('Lansing',1)
```
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/athena/insertTable.png" alt="Athena connection" />
### Select Operation
This query retrieves all records from the *student* table where the age of the student is exactly 1 year.
```sql
SELECT * from student WHERE AGE=1
```
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/athena/selectOperation.png" alt="Athena connection" />
### List Tables
This query is used to display a list of all tables in the current database.
```sql
SHOW TABLES
```
<img style={{ border:'0', marginBottom:'15px'}} className="screenshot-full" src="/img/datasource-reference/athena/listTables.png" alt="Athena connection" />
</div>

View file

@ -0,0 +1,142 @@
---
id: azureblobstorage
title: Azure Blob
---
ToolJet offers the capability to establish a connection with Azure Blob storage in order to read and store large objects.
## Connection
To establish a connection with the Azure Blob 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 Azure Blob as the data source.
ToolJet requires the following to connect to your Azure Blob.
- **Connection String**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/gdsazure-v2.png" alt="Azure Blob - ToolJet" />
</div>
<div style={{paddingTop:'24px'}}>
## Querying Azure Blob
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Azure Blob** datasource added in previous step.
3. Select the desired **operation** from the dropdown and enter the required **parameters**.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
:::tip
Query results can be transformed using Transformation. For more information on transformations, please refer to our documentation at **[link](/docs/beta/app-builder/custom-code/transform-data)**.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/queries-v2.png" alt="Azure Blob - ToolJet" />
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
1. **[Create container](#create-container)**
2. **[List containers](#list-containers)**
3. **[List blobs](#list-blobs)**
4. **[Upload blob](#upload-blob)**
5. **[Read blob](#read-blob)**
6. **[Delete blob](#delete-blob)**
### Create Container
The create container operation enables the creation of new containers within Azure Blob storage. Containers serve as logical units for organizing and managing blob data. Users can provide a unique name for the container. Once created, the container is available for use in storing and organizing blob data. If the container with the same name already exists, the operation fails.
#### Required Parameters
- **Container Name**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/createcontainer-v2.png" alt="Azure blob: create container operation" style={{marginBottom:'15px'}}/>
</div>
### List Containers
The list container operation allows you to retrieve a list of containers within Azure Blob storage.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/listcon-v2.png" alt="Azure blob: list container operation" style={{marginBottom:'15px'}}/>
</div>
### List Blobs
The list blobs operation enables you to retrieve a list of blobs within a specific container in Azure Blob storage.
#### Required Parameter
- **Container**
- **Page Size**
#### Optional Parameters
- **Prefix**
- **Continuation Token**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/listblobs-v2.png" alt="Azure blob: list blobs operation" style={{marginBottom:'15px'}}/>
</div>
### Upload Blob
The upload blob operation allows you to upload a new blob or update an existing blob in Azure Blob storage. It provides a convenient way to store data such as files, images, or documents in the specified container.
#### Required Parameters
- **Container**
- **Blob Name**
- **Content Type**
- **Upload Data**
- **Encoding**
<img className="screenshot-full" src="/img/datasource-reference/azureblob/uploadBlob.png" alt="Azure blob: upload blobs operation" style={{marginBottom:'15px'}}/>
### Read Blob
The read blob operation allows you to retrieve the content of a specific blob stored in Azure Blob storage. It enables you to access and retrieve the data stored within the blob for further processing or display.
#### Required Parameters
- **Container**
- **Blob Name**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/read-v2.png" alt="Azure blob: read blob operation" style={{marginBottom:'15px'}} />
</div>
### Delete Blob
The delete blob operation allows you to remove a specific blob from Azure Blob storage. This operation permanently deletes the blob and its associated data, freeing up storage space and removing the blob from the container.
#### Required Parameters
- **Container**
- **Blob Name**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/delete-v2.png" alt="Azure blob: delete blob operation" />
</div>
</div>

View file

@ -0,0 +1,292 @@
---
id: baserow
title: Baserow
---
ToolJet can connect to your Baserow account to read and write data.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **Baserow** 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 through the ToolJet dashboard.
ToolJet requires the following to connect to Baserow:
- **API token**
- **Host**
- **Base URL**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/baserow/baserow-intro-v2.png" alt="Baserow intro" />
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Querying Baserow
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Baserow** datasource added in previous step.
3. Select the desired operation from the dropdown and enter the required parameters.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/baserow/operations.png" alt="Amazon SES" />
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
- **[List fields](#list-fields)**
- **[List rows](#list-rows)**
- **[Get row](#get-row)**
- **[Create row](#create-row)**
- **[Update row](#update-row)**
- **[Move row](#move-row)**
- **[Delete row](#delete-row)**
### List Fields
This query lists all the fields in a table.
#### Required Parameter
- **Table ID**
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-list-fields-v2.png" alt="Baserow list fields" />
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```yaml
[
{
"id": 331156,
"table_id": 57209,
"name": "Name",
"order": 0,
"type": "text",
"primary": true,
"text_default": ""
},
{
"id": 331157,
"table_id": 57209,
"name": "Last name",
"order": 1,
"type": "text",
"primary": false,
"text_default": ""
},
{
"id": 331158,
"table_id": 57209,
"name": "Notes",
"order": 2,
"type": "long_text",
"primary": false
},
{
"id": 331159,
"table_id": 57209,
"name": "Active",
"order": 3,
"type": "boolean",
"primary": false
}
]
```
</details>
### List Rows
This query lists all the rows in a table.
#### Required Parameter
- **Table ID**
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-list-rows-v2.png" alt="Baserow list"/>
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 2,
"order": "0.99999999999999999991",
"Name": "Bill",
"Last name": "Gates",
"Notes": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce dignissim, urna eget rutrum sollicitudin, sapien diam interdum nisi, quis malesuada nibh eros a est.",
"Active": false
},
{
"id": 3,
"order": "0.99999999999999999992",
"Name": "Mark",
"Last name": "Zuckerberg",
"Notes": null,
"Active": true
},
{
"id": 1,
"order": "0.99999999999999999997",
"Name": "Elon",
"Last name": "Musk",
"Notes": null,
"Active": true
}
]
}
```
</details>
### Get Row
#### Required Parameters
- **Table ID**
- **Row ID**
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-get-row-v2.png" alt="Baserow get" />
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"id": 1,
"order": "0.99999999999999999997",
"Name": "Elon",
"Last name": "Musk",
"Notes": null,
"Active": true
}
```
</details>
### Create Row
#### Required Parameters
- **Table ID**
- **Records**
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-create-row-v2.png" alt="Bserow create"/>
#### Example
```json
{
"Name": "Test",
"Last name": "Test Name",
"Notes": "Test Note",
"Active": true
}
```
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"id": 19,
"order": "0.99999999999999999996",
"Name": "Test",
"Last name": "Test Name",
"Notes": "Test Note",
"Active": true
}
```
</details>
### Update Row
#### Required Parameters
- **Table ID**
- **Row ID**
- **Records**
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-update-row-v2.png" alt="Baserow update" />
#### Example
```json
{
"Name": "Test",
"Last name": "Test Name",
"Notes": "Test Note",
"Active": true
}
```
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"id": 19,
"order": "0.99999999999999999996",
"Name": "Test",
"Last name": "Test Name",
"Notes": "Test Note",
"Active": true
}
```
</details>
### Move Row
#### Required Parameters
- **Table ID**
- **Row ID**
#### Optional Parameters
- **Before ID** (The row will be moved before the entered ID. If not provided, then the row will be moved to the end )
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-move-row-v2.png" alt="Baserow move row" />
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"id": 3,
"order": "2.00000000000000000000",
"Name": "Mark",
"Last name": "Zuckerburg",
"Notes": null,
"Active": true
}
```
</details>
### Delete Row
#### Required Parameters
- **Table ID**
- **Row ID**
<img className="screenshot-full" src="/img/datasource-reference/baserow/baserow-delete-row-v2.png" alt="Baserow delete" />
While deleting a row, the response will be either success or failure from Baserow.
</div>

View file

@ -0,0 +1,197 @@
---
id: bigquery
title: BigQuery
---
ToolJet can connect to **BigQuery** databases to run BigQuery queries.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **BigQuery** 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 BigQuery as the data source.
ToolJet requires the following to connect to your BigQuery:
- **Private key**
How to get a Private key?
1. You need to enable BigQuery API in your Google Cloud Console. You can follow the steps to enable BigQuery API from **[Google Cloud](https://cloud.google.com/bigquery/docs/bigquery-web-ui)**.
2. You need to create a service account and generate a key for the same. You can follow the steps to create a service account from **[Google Cloud](https://cloud.google.com/iam/docs/creating-managing-service-accounts)**.
3. Once you have created the service account after following the steps mentioned in the Google Cloud guide, create a new **Key** and download it in a JSON file.
4. Now, copy and paste the data from the downloaded JSON file into the **Private key** field in the BigQuery data source form.
<img className="screenshot-full" src="/img/datasource-reference/bigquery/bq-create-v2.png" alt="BQ create" />
**The JSON file should look like this:**
```json
{
"type": "service_account",
"project_id": "long-sonar-324407",
"private_key_id": "63f4415e600bd7879bc14fd1157a4aabe227c204",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRGgDmfwYcKp4q\n3ce4DkrKv0vTn/Jn2Z2vEHp+oOz5ebZqmE3v56c6YIvtVRblANILPrOsB5ZvkF5f\nEzZBXn7ZI3+dqKBrpxbJqF6bKTLENdgFZRTbXHtGDpmwX4A+ufir9QNoezRw0i5L\nnVZiVC54f/Qt/cKT8794qSnrxNX1TneZLGxJWou9VAl3xT9h2HdL56gYIuleWXDK\nnXkb3Leh9AMZCdFPMyC24MWefWrUbNkqJ7V8FHo7bMrAcFNuSoF2NfK1v6IPLkEs\nwAU0CJ9VSg6rrahQOqIJ04cdYs2OUh4lRvRB6pqlVvtl6EdJB6dHln1nDzpgHbnb\n+acfwEDnAgMBAAECggEAGs/mSKgGDQuL73wztU6j2X6RBwhN6XIWjZGj22PgLxcj\nxGRWLgp6v3oMxzhvcJrb1BRMrqTkbdbJuxA4F0a6JjaukPVD6Lnqqp37z5KHT3CG\nDB8LfxtLNU7+9wYv6Bspn0cSEk4mCcdxp0F8B6y6rrndgh41WopZRWwPk4tQUh1r\nor67AAYd3rtzGMLoghs+8UE+UYa8wbpsbmHEYgqvXQAkNsl8WdNwqmI0G4lf+pgx\n7Rm27LJrtdBBHc48RUhg2eiN05HLCsnwkrnSj0rLL/L7T1yoSfCSUuv1mTUesxQ1\nXUEsPQQTTsNsqKOxT71CzQLElrPfwZkN4Y/IOJqX3QKBgQD6u0idi2r54hMjBSuk\npLgXygH5AWfHc4QqMCui7HZrFOJ4U4AreI/zZrM3Gemgs+1l27wsUjoxADW2Egyq\nX5AVe94RKSV3cCIIty38VOUBVsgyxj38d8yWkpJKJ2FcAgqEzPDDo0TCaOEq01oA\nYqjkgBz7Sh4XhQ5xwzfnOPRPtQKBgQDVfsly/k03wAJo1xlUZeq9mAnba5Hz07x9\nJ3REAwrtOaD891rKbkqDZKdGHTMweFGeEW2Hx7Q5iRS4WDKFO14wgSHFTkkVoSKR\n2W7XMomUQPFojQwgkDhrxsGE8O1DqfQ0+A5AJn2ASv/cyVGE3V2xg2rGr/HWi6Wq\nUp4FxebXqwKBgQDNIcCNNG03N6EUe7xzHViIDfuDL4UqhvXQVky9JNzVSubmLtqj\ntiV/q7xgDlE36z0EorvXPwbg5B0NcsLt+PU2vnq2a4V9rD4MB2IWGZaqe8ea0toP\n3iuB3TTWelWLIxhcAhfQ15j/vTLLCNOPkShAmhgb902bTH6+0ErCX7RyKQKBgQCe\nDOeLpvF5VT8zaBILZgva4eRiOQdqz5RZvsyW0P3U0vX4cBIZjH7DOM+Q22sa9efO\nMi6490HX2kCpnDmCYon/NInQrHz0cz7JZINm8rXhOBa/hLO2o63xM8nt5gJwNjBg\nykaafSQpxtwWEj+0McD7+kMg5f4OC4HQTqtHsNONUwKBgAoWGGRPja068BPIiUMB\nezsdYPP5TdASiBeAEPaQXQHlJxPDu9KoKqM5xvWIdR8eH1z7cuQ3RP89hYT03/UT\nBvWXHk2MJQZK7BZDw9KMZAKexK9/qxwHS6i7HhErD+Au3UaRX8dfjJzX8WAwuAwp\nVDwHncN3n4mPFQl7eijnQZ/F\n-----END PRIVATE KEY-----\n",
"client_email": "tooljettest@long-sonar-324407.iam.gserviceaccount.com",
"client_id": "103664451567222591066",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/tooljettest%40long-sonar-324407.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
```
</div>
<div style={{paddingTop:'24px'}}>
## Querying BigQuery
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **BigQuery** datasource added in previous step.
3. Select the desired operation from the dropdown and enter the required parameters.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/bigquery/bq-query-v2.png" alt="BQ query" />
</div>
:::tip
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/beta/app-builder/custom-code/transform-data)
:::
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
- **[Query](#query)**
- **[List Datasets](#list-datasets)**
- **[List Tables](#list-tables)**
- **[Insert Record ](#insert-record)**
- **[Delete Record ](#delete-record)**
- **[Update Record](#update-record)**
- **[Create View](#create-view)**
- **[Create Table](#create-table)**
- **[Delete Table](#create-table)**
### Query
This operation returns the data based on the **Query**.
**Note**: Follow the reference given in **Google Cloud** about the operations: **[Query options](https://cloud.google.com/bigquery/docs/reference/rest/v2/Job)** and **[Query results options](https://cloud.google.com/nodejs/docs/reference/bigquery/latest/overview#_google_cloud_bigquery_QueryResultsOptions_type)**.
#### Required Parameters
- **Query**
- **Query options**
- **Query results options**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/query-v2.png" alt="BQ query" style={{marginBottom:'15px'}}/>
### List Datasets
This operation returns the list of datasets.
<img className="screenshot-full" src="/img/datasource-reference/bigquery/list-datasets-v2.png" alt="BQ list datasets" style={{marginBottom:'15px'}}/>
### List Tables
This operation returns the list of tables within a dataset.
#### Required Parameter
- **Dataset ID**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/listtables-v2.png" alt="BQ list tables" style={{marginBottom:'15px'}}/>
### Create Table
This operation is used to create a table.
#### Required Parameters
- **Table ID**
- **Dataset ID**
- **Options**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/create-table.png" alt="BQ create tables"/>
**NOTE:** Visit https://github.com/googleapis/nodejs-bigquery/blob/main/samples/createTable.js for more info on schema.
### Delete Table
This operation is used to delete a table.
#### Required Parameters
- **Table ID**
- **Dataset ID**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/delete-table.png" alt="BQ delete tables" style={{marginBottom:'15px'}}/>
### Create View
This operation is used to create a view.
#### Required Parameters
- **Table ID**
- **Dataset ID**
- **View name**
- **View columns**
- **Condition**
- **Query options**
- **Query results options**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/create-view-v2.png" alt="BQ create view" style={{marginBottom:'15px'}}/>
### Insert Record
This operation is used to insert a record.
#### Required parameters:
- **Table ID**
- **Dataset ID**
- **Rows**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/bq-insert-v2.png" alt="BQ insert" style={{marginBottom:'15px'}}/>
### Delete Record
Use this operation to delete a record.
#### Required parameters:
- **Table ID**
- **Dataset ID**
- **Condition**
- **Query options**
- **Query results options**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/bq-delete-v2.png" alt="BQ delete" />
:::warning
NOTE: Be careful when deleting records in a table. If you omit the WHERE clause, all records in the table will be deleted!
:::
### Update Record
Use this operation to update a record.
#### Required parameters:
- **Table ID**
- **Dataset ID**
- **Columns**
- **Condition**
- **Query results options**
<img className="screenshot-full" src="/img/datasource-reference/bigquery/bq-update-v2.png" alt="BQ update" />
</div>

View file

@ -0,0 +1,174 @@
---
id: clickhouse
title: ClickHouse
---
ToolJet can connect to the ClickHouse to read and write data.
:::info
ToolJet uses this [NodeJS](https://github.com/TimonKK/clickhouse) client for ClickHouse.
:::
## Connection
To establish a connection with the Clickhouse 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 through the ToolJet dashboard.
ToolJet requires the following to connect to your ClickHouse Database:
- **Username**
- **Password**
- **Host**
- **Port**
- **Database Name**
- **Protocol**
- **Use Post**
- **Trim Query**
- **Use Gzip**
- **Debug**
- **Raw**
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/connection-v2.png" alt="ClickHouse connection" />
<div style={{paddingTop:'24px'}}>
## Querying ClickHouse
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **ClickHouse** datasource added in previous step.
3. Select the operation you want to perform and enter the query.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to create and trigger the query.
:::info
For more details on clickhouse visit [Clickhouse Docs](https://clickhouse.com/docs/en/quick-start).
:::
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
- **[SQL Query](#sql-query)**
- **[Insert array of objects](#insert-array-of-objects)**
### SQL Query
Use this to operation to enter **[ClickHouse SQL Statements](https://clickhouse.com/docs/en/sql-reference/statements/)**. These statements represent various kinds of action you can perform using SQL queries.
#### Example SQL queries
#### SELECT:
```sql
SELECT * from test array;
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/select-v2.png" alt="ClickHouse SQL Statement operation" style={{marginBottom:'15px'}}/>
#### CREATE:
```sql
CREATE TABLE test array3 (
date Date,
str String,
arr Array(String),
arr2 Array (Date)
arr3 Array(UInt32) ,
id1 UUID
)ENGINE=MergeTree () ORDER BY(str)
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/create-v2.png" alt="ClickHouse SQL Statement operation" style={{marginBottom:'15px'}}/>
#### ALTER TABLE (add column)
```sql
ALTER TABLE test array1 ADD COLUMN Added2 UInt32;
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/alter-v2.png" alt="ClickHouse SQL Statement operation" style={{marginBottom:'15px'}} />
#### SELECT WITH WHERE CLAUSE
```sql
SELECT * FROM test array1 WHERE str='Somethingl...'
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/selectwithwhere-v2.png" alt="ClickHouse SQL Statement operation" style={{marginBottom:'15px'}} />
#### UPDATE
```sql
ALTER TABLE test_array1 UPDATE arr = (12] WHERE str='Somethingl...'
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/update-v2.png" alt="ClickHouse SQL Statement operation" style={{marginBottom:'15px'}} />
#### DELETE
```sql
ALTER TABLE test_array1 DELETE WHERE str= 'Somethingl...'
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/delete-v2.png" alt="ClickHouse SQL Statement operation" style={{marginBottom:'15px'}} />
#### NORMAL INSERT
##### Step 1 - Creating Table
```sql
CREATE TABLE test array4 (
name String,
date Date
)ENGINE=MergeTree () ORDER BY (name)
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/step1-v2.png" alt="ClickHouse SQL Statement operation" />
#### Step 2 - Insert
```sql
INSERT INTO test_array4 (*) VALUES ('juvane', '1996-01-13')
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/step2-v2.png" alt="ClickHouse SQL Statement operation" />
:::info
**Giving Primary Key**
```sql
CREATE TABLE db.table_name
(
name1 type1, name2 type2, ...,
PRIMARY KEY(expr1[, expr2,...])]
)
ENGINE = engine;
OR
CREATE TABLE db.table_name
(
name1 type1, name2 type2, ...
)
ENGINE = engine
PRIMARY KEY(expr1[, expr2,...]);
```
:::
### Insert Array of Objects
Use this operation for inserting array of objects.
#### Required Parameters:
- **Body**
- **Table name**
- **Fields**
**Example Body value:**
```javascript
[
{ "id": 1, "name": "Alice", "age": 25 },
{ "id": 2, "name": "Bob", "age": 30 },
{ "id": 3, "name": "Charlie", "age": 28 }
]
```
<img className="screenshot-full" src="/img/datasource-reference/clickhouse/insertarray-v2.png" alt="ClickHouse Insert array of objects operation" />
</div>

View file

@ -0,0 +1,128 @@
---
id: cosmosdb
title: CosmosDB
---
ToolJet can connect to CosmosDB databases to read and write data.
## Connection
To establish a connection with the CosmosDB 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 through the ToolJet dashboard.
ToolJet requires the following to connect to your Cosmos DB.
- **Cosmos DB Account End point**
- **Cosmos DB Account Key**
:::info
**Azure Cosmos DB End Point** is the URL of the Cosmos DB service.
**Azure Cosmos DB Key** is the key that is used to access the Cosmos DB service.
You can find the endpoint and key in the **[Azure Portal](https://portal.azure.com/)**.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/cosmosdb-connect-v2.png" alt="ToolJet - Data source - CosmosDB"/>
</div>
<div style={{paddingTop:'24px'}}>
## Querying CosmoDB
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **CosmoDB** datasource added in previous step.
3. Select the operation you want to perform and enter the query.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to create and trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/operations.png" alt="ToolJet - Data source - CosmosDB"/>
</div>
<div style={{paddingTop:'24px'}}>
## Supported Queries
- **[List databases](#list-databases)**
- **[List containers](#list-containers)**
- **[Insert items](#insert-items)**
- **[Read item](#read-item)**
- **[Delete item](#delete-item)**
- **[Query database](#query-database)**
### List Databases
This query lists all the databases in a Cosmos DB.
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/listDatabase.png" alt="ToolJet - Data source - CosmosDB" style={{marginBottom:'15px'}}/>
### List Containers
This query lists all the containers of a database in a Cosmos DB.
#### Required Parameter
- **Database**
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/listContainers.png" alt="ToolJet - Data source - CosmosDB" style={{marginBottom:'15px'}}/>
### Insert Items
This query inserts one or more items in a container of a database in a Cosmos DB.
#### Required Parameter
- **Database**
- **Container**
- **Items**
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/insertItems.png" alt="ToolJet - Data source - CosmosDB" style={{marginBottom:'15px'}}/>
#### Example
```yaml
{
"id": "123",
"product": "Laptop",
"price": 1200,
"customer_id": "C001"
}
```
### Read Item
To read a single item from a container of a database in a Cosmos DB, use the following query.
#### Required Parameter
- **Database**
- **Container**
- **Item ID**
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/readItem.png" alt="ToolJet - Data source - CosmosDB" style={{marginBottom:'15px'}}/>
### Delete Item
To delete an item from a container of a database in a Cosmos DB, use the following query.
#### Required Parameter
- **Database**
- **Container**
- **Item ID**
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/deleteItem.png" alt="ToolJet - Data source - CosmosDB" style={{marginBottom:'15px'}}/>
### Query Database
To query documents from a container of a database in a Cosmos DB using SQL-like syntax, use the following query.
#### Required Parameter
- **Database**
- **Container**
- **Query**
<img className="screenshot-full" src="/img/datasource-reference/cosmosdb/queryDatabase.png" alt="ToolJet - Data source - CosmosDB" style={{marginBottom:'15px'}}/>
#### Example
```yaml
SELECT * FROM c WHERE c.age > 20 AND c.age <= 30
```
</div>

View file

@ -0,0 +1,284 @@
---
id: couchdb
title: CouchDB
---
ToolJet can connect to CouchDB databases to read and write data.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the CouchDB 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 through the ToolJet dashboard.
ToolJet requires the following to connect to your CouchDB.
- **Username**
- **Password**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/connections.png" alt="Couch listing"/>
</div>
<div style={{paddingTop:'24px'}}>
## Querying CouchDB
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **CouchDB** datasource added in previous step.
3. Select the operation you want to perform and enter the query.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to create and trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/couchdb/operations.png" alt="Couch listing"/>
</div>
<div style={{paddingTop:'24px'}}>
## Supported Queries
- **[List Records](#list-records)**
- **[Retrieve Record](#retrieve-record)**
- **[Create Record](#create-record)**
- **[Update Record](#update-record)**
- **[Delete Record](#delete-record)**
- **[Find](#find)**
- **[Get View](#get-view)**
### List Records
This query lists all the records in a database.
#### Optional Parameters
- **Include docs**
- **Descending order**
- **Limit**
- **Skip**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/listing-v2.png" alt="Couch listing"/>
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"total_rows": 3,
"offset": 0,
"rows": [
{
"id": "23212104e60a71edb42ebc509f000dc2",
"key": "23212104e60a71edb42ebc509f000dc2",
"value": {
"rev": "1-0cc7f48876f15883394e5c139c628123"
}
},
{
"id": "23212104e60a71edb42ebc509f00216e",
"key": "23212104e60a71edb42ebc509f00216e",
"value": {
"rev": "1-b3c45696b10cb08221a335ff7cbd8b7a"
}
},
{
"id": "23212104e60a71edb42ebc509f00282a",
"key": "23212104e60a71edb42ebc509f00282a",
"value": {
"rev": "1-da5732beb913ecbded309321cac892d2"
}
},
]
}
```
</details>
### Retrieve Record
This operation fetches a single record by its record ID.
#### Required Parameters:
- **Record ID**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/retrieving-v2.png" alt="Couch retrieve view" />
<details id="tj-dropdown">
<summary> **Response Example** </summary>
```json
{
"_id": "e33dc4e209689cb0400d095fc401a1e0",
"_rev": "1-a62af8e14451af88c150e7e718b7a0e8",
"0": {
"name": "test data"
}
}
```
</details>
### Create Record
Inserts a new record into the database.
#### Required Parameters:
- **Records**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/creating-v2.png" alt="Couch create view"/>
#### Example
```json
[{"name":"tooljet"}]
```
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"ok": true,
"id": "23212104e60a71edb42ebc509f0049a2",
"rev": "1-b0a625abc4e21ee554737920156e911f"
}
```
</details>
### Update Record
You can get the revision id value, by sending a GET request to get the document details.
You get the document as JSON in the response. For each update to the document, the revision field "_rev" gets changed.
#### Required Parameters:
- **Record ID**
- **Revision ID**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/updating-v2.png" alt="Couch update view" />
#### Example
```json
[{"name":"tooljet"}]
```
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"ok": true,
"id": "23212104e60a71edb42ebc509f0049a2",
"rev": "2-b0a625abc4e21ee554737920156e911f"
}
```
</details>
### Delete Record
Removes a record from the database by its record ID.
#### Required Parameters:
- **Record ID**
- **Revision ID**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/deleteRecord.png" alt="Couch delete view"/>
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"ok": true,
"id": "rev_id=2-3d01e0e87139c57e9bd083e48ecde13d&record_id=e33dc4e209689cb0400d095fc401a1e0",
"rev": "1-2b99ef28c03e68ea70bb668ee55ffb7b"
}
```
</details>
### Find
Find documents using a declarative JSON querying syntax.
#### Required Parameters:
- **Mangoquery**
:::info
NOTE:
selector syntax: https://pouchdb.com/guides/mango-queries.html
:::
<img className="screenshot-full" src="/img/datasource-reference/couchdb/find-v2.png" alt="Couch find" />
#### Example
```json
{
"selector": {
"year": {"$gte": 2015}
},
"fields": ["year"]
}
```
Example response from CouchDB:
<img className="screenshot-full" src="/img/datasource-reference/couchdb/find_response.png" alt="Couch find response" />
### Get View
Views are the primary tool used for querying and reporting on CouchDB documents.
#### Required Parameters
- **View url**
#### Optional Parameters:
- **Start key**
- **End key**
- **Limit**
- **Skip**
<img className="screenshot-full" src="/img/datasource-reference/couchdb/get_view-v2.png" alt="Couch get view" />
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"total_rows": 4,
"offset": 0,
"rows": [
{
"id": "23212104e60a71edb42ebc509f000dc2",
"key": "23212104e60a71edb42ebc509f000dc2",
"value": {
"rev": "1-0cc7f48876f15883394e5c139c628123"
}
},
{
"id": "23212104e60a71edb42ebc509f00216e",
"key": "23212104e60a71edb42ebc509f00216e",
"value": {
"rev": "1-b3c45696b10cb08221a335ff7cbd8b7a"
}
},
{
"id": "23212104e60a71edb42ebc509f00282a",
"key": "23212104e60a71edb42ebc509f00282a",
"value": {
"rev": "1-da5732beb913ecbded309321cac892d2"
}
},
{
"id": "23212104e60a71edb42ebc509f002cbd",
"key": "23212104e60a71edb42ebc509f002cbd",
"value": {
"rev": "1-ca5bb3c0767eb42ea6c33eee3d395b59"
}
}
]
}
```
</details>
</div>

View file

@ -0,0 +1,176 @@
---
id: run-js
title: Run JavaScript Code
---
The **Run JavaScript Code** feature in ToolJet allows custom JavaScript code to be executed to enhance application interactivity. This feature is useful for performing calculations, generating values, or interacting with queries and components.
<div style={{paddingTop:'24px'}}>
## Creating a Run JavaScript Query
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select **Run JavaScript Code** from the list of available data sources.
3. Add the JavaScript Code.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/create-query.png" alt="Run JavaScript code" />
</div>
<div style={{paddingTop:'24px'}}>
## Parameters in Run JavaScript Code
Parameters allow for dynamic control over the JavaScript code execution without altering the core script. This provides flexibility by allowing the same code to execute with different inputs.
Each parameter requires:
- **Name**: Name for the parameter
- **Default value**: The value can be constant strings, numbers and object.
### Steps to Add a Parameter
1. In the RunJS query editor, click the **Parameters +** button to create a new parameter.
2. Provide a **Name** for the parameter.
3. Set a Default **Value**, which can be a string, number, or object.
Once added, the **parameter can be referenced in the code using the syntax**: `parameters.<name>`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/js-param.png" alt="Run JavaScript code" />
</div>
### Displaying a Parameter Value in an Alert Box
Let's create a new parameter named _newAlert_ and set the value as object `Displaying the Parameter Value in an Alert Box` and use the alert js method to show the value on the pop-up.
Syntax:
```
alert(parameters.newAlert)
```
When the query is triggered the alert will show the parameters value.
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/param-alert.png" alt="Run JavaScript code" />
### Calling Another Query with Parameters
Parameters can also be used to trigger other queries and pass custom values. Below is an example of how to call one query from another by providing custom parameters.
1. Begin by creating a new RunJS query named _multiply_.
- In this query, add the following parameters:
- _num1_ with a default value of **10**
- _num2_ with a default value of **2**.
- Add the following JavaScript Code:
```javascript
return parameters.num1 * parameters.num2;
```
- To display the result, place a text component on the canvas and set its text to `{{queries.multiply.data}}`.
<br/>
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/multiply-v2.png" alt="Run JavaScript code" />
2. Now, let's create another RunJS query called _callMultiply_, where we will invoke the _multiply_ query created earlier using custom parameter values. Here's the code snippet for _callMultiply_:
```js
queries.multiply.run({ num1: 20, num2: 7 });
```
By executing this code within _callMultiply_, we trigger the _multiply_ query with specific values for its parameters.
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/call-multiply-v2.png" alt="Run JavaScript code" />
With this setup, the _multiply_ query can be called from other queries, such as _callMultiply_, by providing custom parameter values. This allows you to reuse the _multiply_ query with different inputs and display the results accordingly.
</div>
<div style={{paddingTop:'24px'}}>
## RunJS Example Queries
### Generating a Random Number
This example demonstrates how to generate and display a random number using JavaScript.
1. Drag a **button** and a **text** widget inside a **container** widget.
2. Click on the **+ Add** on the query panel to create a query and select **Run JavaScript code** from the available datasources.
3. Write the code in **JavaScript editor** and run the query.
```js
const a = Math.floor(Math.random() * (10 - 1)) + 1;
return a;
```
4. Edit the properties of widgets:
1. Add an event handler to the button:
1. Select event as **On Click**
2. Action as **Run Query**
3. Select the _runjs1_ query that we created. This will run the JavaScript code every time the button is clicked.
2. Edit the property of text widget:
1. In the text field enter **Random number:** `{{queries.runjs1.data}}`. It will display the output as Random number: _result from JS code_
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/random-num.png" alt="Run JavaScript code" />
### Generating a Unique ID
The following code generates a unique ID in the format "id" followed by a sequence of random hexadecimal characters.
```js
var id = "id" + Math.random().toString(16).slice(2);
return id;
```
For example, it could be something like "id2f4a1b".
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/unique-id-1.png" alt="Run JavaScript code" />
### Generating a Timestamp-Based Unique ID
In this code, the resulting ID will have the format "timestamp + randomHex", where "timestamp" is the current time in base-32 and "randomHex" is a random hexadecimal value.
```js
return String(Date.now().toString(32) + Math.random().toString(16)).replace(
/\./g,
""
);
```
This ID will be longer than the one generated earlier, and it could look like "2g3h1d6a4h3".
<img className="screenshot-full" src="/img/datasource-reference/custom-javascript/unique-id-2.png" alt="Run JavaScript code" />
:::tip Resources
- You can also write custom JavaScript code to get the data from **External APIs** and manipulate the response for graphical representation. Here's the [tutorial](https://blog.tooljet.ai/build-github-stars-history-app-in-5-minutes-using-low-code/) on how we used custom JavaScript code to build an app using GitHub API.
- [Import external libraries](/docs/how-to/import-external-libraries-using-runjs) using RunJS.
- [Intentionally Fail](/docs/how-to/intentionally-fail-js-query) a RunJS query.
- [Trigger query at specified intervals](/docs/how-to/run-query-at-specified-intervals) using RunJS.
:::
</div>
<div style={{paddingTop:'24px'}}>
## Libraries
ToolJet allows you to internally utilize these libraries:
| Name | Documentation |
| ------ | ---------------------------------------------------------------------- |
| Moment | [https://momentjs.com/docs/](https://momentjs.com/docs/) |
| Lodash | [https://lodash.com/docs/](https://lodash.com/docs/) |
| Axios | [https://axios-http.com/docs/intro](https://axios-http.com/docs/intro) |
:::info
Issues with writing custom JavaScript code? Ask in our [Slack Community](https://join.slack.com/t/tooljet/shared_invite/zt-2rk4w42t0-ZV_KJcWU9VL1BBEjnSHLCA).
:::
</div>

View file

@ -0,0 +1,137 @@
---
id: databricks
title: Databricks
---
Databricks is a cloud-based platform for data processing, analytics, and machine learning. ToolJet connects to Databricks, allowing your applications to access and update your data in your Databricks Warehouses directly using SQL queries.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', borderRadius:'5px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)' }} className="screenshot-full" src="/img/datasource-reference/databricks/install.gif" alt="Install Databricks" />
</div>
<div style={{paddingTop:'24px'}}>
## Configuration
ToolJet's Databricks integration relies on a configuration form that supports the following parameters:
#### Required Parameters
- **Server hostname**: The server hostname or the IP address of your Databricks Warehouse. For example, `62596234423488486.6.gcp.databricks.com`.
- **HTTP Path**: The API endpoint path for the Databricks resource you want to access. For example, `/sql/1.0/warehouses/44899g7346c19m95`.
- **Personal access token**: Personal access tokens are used for secure authentication to the Databricks API instead of passwords. For example, `dapi783c7d155d138d8cf14`.
#### Optional Parameters
- **Port**: The port number of your Databricks Warehouse. The default port number is `443`.
- **Default Catalog**: The default catalog to use for the connection.
- **Default Schema**: The default schema to use for the connection.
### Setup
- Navigate to your Databricks workspace, select the desired SQL Warehouse, and find **Server Hostname** and **HTTP Path** within the connection details tab.
<img style={{ border:'0', borderRadius:'5px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)' }} className="screenshot-full" src="/img/datasource-reference/databricks/connection-details.png" alt="Databricks: Connection Details" />
- To generate a personal access token, access your Databricks User Settings, select the Developer tab, click Manage under Access Tokens, and then click on the **Generate New Token** button.
<img style={{ border:'0', borderRadius:'5px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)' }} className="screenshot-full" src="/img/datasource-reference/databricks/generate-token.png" alt="Databricks: Access Tokens" />
- Navigate to the Databricks datasource configuration form in ToolJet, fill in the required parameters, and click the **Save** button. You can test the connection by clicking the **Test Connection** button.
<img style={{ border:'0', borderRadius:'5px', boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)' }} className="screenshot-full" src="/img/datasource-reference/databricks/setup-parameters.png" alt="Databricks: Setup Paramaters" />
</div>
<div style={{paddingTop:'24px'}}>
## Querying Databricks
1. Click on + Add button of the query manager at the bottom panel of the editor.
2. Select the **Databricks** datasource added in previous step.
3. Select the **SQL Mode** from the dropdown. (ToolJet currently supports only SQL mode for Databricks interactions.)
4. Click on the **Preview** button to preview the output or Click on the **Run** button to create and trigger the query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/databricks/add-query.gif" alt="Databricks: Query Setup" />
</div>
:::tip
You can apply transformations to the query results. Refer to our transformations documentation for more information: [link](/docs/beta/app-builder/custom-code/transform-data)
:::
</div>
<div style={{paddingTop:'24px'}}>
## Supported Queries
Databricks supports standard SQL commands for data manipulation tasks.
### Read Data
The following example demonstrates how to read data from a table. The query selects all the columns from the _customers_ table.
```sql
SELECT * FROM customers
```
<img className="screenshot-full" src="/img/datasource-reference/databricks/readData.png" alt="Databricks: Read Data Query" style={{marginBottom:'15px'}}/>
### Write Data
The following example demonstrates how to write data to a table. The query inserts a new row into the _customers_ table.
```sql
INSERT INTO customers (
customer_id,
first_name,
last_name,
email,
phone,
city,
state,
zip_code,
country
) VALUES (
'1001'
'Tom',
'Hudson',
'tom.hudson@example.com',
'50493552',
'San Clemente',
'CA',
'92673',
'USA'
);
```
<img className="screenshot-full" src="/img/datasource-reference/databricks/writeData.png" alt="Databricks: Write Data Query" style={{marginBottom:'15px'}}/>
### Update Data
The following example demonstrates how to update data in a table. The query updates the _first_name_ and _email_ column of the _customers_ table.
```sql
UPDATE customer
SET first_name = 'John',
email = 'john.hudson@example.com'
WHERE customer_id = 1001;
```
<img className="screenshot-full" src="/img/datasource-reference/databricks/updateData.png" alt="Databricks: Update Data Query" style={{marginBottom:'15px'}}/>
### Delete Data
The following example demonstrates how to delete data from a table. The query deletes a row from the _customers_ table.
```sql
DELETE FROM customer
WHERE customer_id = 1001;
```
<img className="screenshot-full" src="/img/datasource-reference/databricks/deleteData.png" alt="Databricks: Delete Data Query" style={{marginBottom:'15px'}}/>
</div>

View file

@ -0,0 +1,312 @@
---
id: dynamodb
title: DynamoDB
---
**DynamoDB** is a managed non-relational database service provided by Amazon. ToolJet has the capability to connect to DynamoDB for reading and writing data.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **DynamoDB** 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 through the ToolJet dashboard.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/dynamodb/dynamogds-v2.png" alt="DynamoDB" />
</div>
ToolJet supports connecting to DynamoDB using three methods: **IAM Credentials**, **AWS Instance Credentials**, or **AWS ARN Role**.
When using **IAM Credentials**, you will need to provide the following information:
- **Region**
- **Access key**
- **Secret key**
It is recommended to create a dedicated IAM user for the database in order to have granular control over ToolJet's access levels.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/dynamodb/iam-v2.png" alt="ToolJet - DynamoDB connection" />
</div>
To connect to DynamoDB using an **AWS Instance Credentials**, select the option to **Use AWS Instance Credentials**. This will utilize the IAM role attached to the EC2 instance where ToolJet is running. The WebIdentityToken parameter obtained from a successful login with an identity provider is used to access the metadata service of an ECS container and the EC2 instance.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/dynamodb/awsinstance-v2.png" alt="ToolJet - DynamoDB connection" />
</div>
If you prefer to use an **AWS ARN Role**, you will need to provide the following details:
- **Region**
- **Role ARN**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/dynamodb/arn-v2.png" alt="ToolJet - DynamoDB connection" />
</div>
</div>
<div style={{paddingTop:'24px'}}>
## Querying DynamoDB
To perform queries on **DynamoDB**, click on the **+ Add** button in the query manager located at the bottom panel of the editor. Select the previously added database as the data source for the query. Choose the desired operation and click on the **Run** button to run the query.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/dynamodb/queries-v2.png" alt="ToolJet - DynamoDB connection" />
</div>
:::tip
You can apply transformations to the query results. Refer to our transformations documentation for more information: [link](/docs/beta/app-builder/custom-code/transform-data)
:::
#### Supported Operations
- **[List Tables](#list-tables)**
- **[Get Item](#get-item)**
- **[Query Table](#query-table)**
- **[Scan Table](#scan-table)**
- **[Delete Item](#delete-item)**
- **[Update Item](#update-item)**
- **[Describe Table](#describe-table)**
- **[Create Table](#create-table)**
- **[Put Item](#put-item)**
</div>
### List Tables
Returns an array of table names associated with the current account and endpoint. The output from _List Tables_ is paginated, with each page returning a maximum of 100 table names.
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/listtables-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Get Item
Retrieves a single item from a table. You must specify the primary key for the item that you want. You can retrieve the entire item, or just a subset of its attributes.
#### Required Parameter
- **Table**
- **Key name**
#### Example
```json
{
"Key": {
"ForumName": {
"S": "Amazon DynamoDB"
},
"Subject": {
"S": "How do I update multiple items?"
}
}
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/getitem-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Query Table
Retrieves all items that have a specific partition key. You must specify the partition key value. You can retrieve entire items, or just a subset of their attributes. Optionally, you can apply a condition to the sort key values so that you only retrieve a subset of the data that has the same partition key. You can use this operation on a table, provided that the table has both a partition key and a sort key. You can also use this operation on an index, provided that the index has both a partition key and a sort key.
#### Required Parameter
- **Query condition**
#### Example
```yaml
{
"TableName": "Reply",
"IndexName": "PostedBy-Index",
"Limit": 3,
"ConsistentRead": true,
"ProjectionExpression": "Id, PostedBy, ReplyDateTime",
"KeyConditionExpression": "Id = :v1 AND PostedBy BETWEEN :v2a AND :v2b",
"ExpressionAttributeValues":
{
":v1": { "S": "Amazon DynamoDB#DynamoDB Thread 1" },
":v2a": { "S": "User A" },
":v2b": { "S": "User C" },
},
"ReturnConsumedCapacity": "TOTAL",
}
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/querytable-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Scan Table
Retrieves all items in the specified table or index. You can retrieve entire items, or just a subset of their attributes. Optionally, you can apply a filtering condition to return only the values that you are interested in and discard the rest.
#### Required Parameter
- **Scan condition**
#### Example
```yaml
{ "TableName": "<table_name>" }
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/scantable-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Delete Item
Deletes a single item from a table. You must specify the primary key for the item that you want to delete.
#### Required Parameter
- **Table**
- **Key name**
#### Example
```yaml
{
"Key":
{
"ForumName": { "S": "Amazon DynamoDB" },
"Subject": { "S": "How do I update multiple items?" },
},
"ConditionExpression": "attribute_not_exists(Replies)",
"ReturnValues": "ALL_OLD",
}
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/deleteitem-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Update Item
Update an item in DynamoDB by specifying the primary key and providing new attribute values. If the primary key does not exist in the table then instead of updating it will insert a new row.
#### Required Parameter
- **Update Condition**
#### Example
```yaml
{
"TableName": "USER_DETAILS_with_local",
"Key": { "USER_ID": 1, "USER_NAME": "Nick" },
"UpdateExpression": "set USER_AGE = :age, USER_FEE = :fee",
"ExpressionAttributeValues": { ":age": 40, ":fee": 230545 },
}
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/updateitem-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Describe Table
This operation in DynamoDB retrieves metadata and configuration details about a specific table. It provides information such as the table's name, primary key schema, provisioned throughput settings, and any secondary indexes defined on the table.
#### Required Parameter
- **Table**
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/describetable-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Create Table
This operation in DynamoDB enables you to create a new table by specifying its name, primary key schema, and optional configurations.
#### Required Parameter
- **Table Parameters**
#### Example
```yaml
{
"AttributeDefinitions":
[
{ "AttributeName": "USER_ID", "AttributeType": "N" },
{ "AttributeName": "USER_FEE", "AttributeType": "N" },
],
"KeySchema": [{ "AttributeName": "USER_ID", "KeyType": "HASH" }],
"LocalSecondaryIndexes":
[
{
"IndexName": "USER_FEE",
"KeySchema":
[
{ "AttributeName": "USER_ID", "KeyType": "HASH" },
{ "AttributeName": "USER_FEE", "KeyType": "RANGE" },
],
"Projection": { "ProjectionType": "KEYS_ONLY" },
},
],
"ProvisionedThroughput": { "ReadCapacityUnits": 1, "WriteCapacityUnits": 1 },
"TableName": "USER_FEE_LOCAL",
"StreamSpecification": { "StreamEnabled": false },
}
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0'}} className="screenshot-full" src="/img/datasource-reference/dynamodb/createtable-v2.png" alt="ToolJet - DynamoDB operations" />
</div>
### Put Item
This operation allows you to create or replace an item in a table. It enables you to specify the table name, provide the attribute values for the new item, and define the primary key attributes to uniquely identify the item.
#### Required Parameter
- **New Item Details**
#### Example
```yaml
{
"TableName": "USER_DETAILS_with_localS",
"Item":
{ "USER_ID": 1, "USER_NAME": "NICK", "USER_AGE": 34, "USER_FEE": 1234.56 },
}
```
<div style={{textAlign: 'center'}}>
<img style={{ border:'0', marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/dynamodb/putitem-v2.png" alt="ToolJet - DynamoDB operations" />
</div>

View file

@ -0,0 +1,489 @@
---
id: elasticsearch
title: Elasticsearch
---
ToolJet allows you to connect to your Elasticsearch cluster to perform data read/write operations and execute various queries.
## Connection
To connect to an Elasticsearch data source in ToolJet, you can either click the **+ Add new data source** button on the query panel or navigate to the **[Data Sources](/docs/data-sources/overview)** page in the ToolJet dashboard.
:::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.
:::
To connect to your Elasticsearch cluster, the following details are required:
- **Host**
- **Port**
- **Username**
- **Password**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/connect-v2.png" alt="Elastic Connect" />
</div>
ToolJet also supports SSL certificate-based connections:
- You can use either CA or Client certificates.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/ssl-v2.png" alt="Elastic SSL Connect" />
</div>
<div style={{paddingTop:'24px'}}>
## Querying Elasticsearch
1. Click the **+ Add** button in the query manager at the bottom of the editor and select the Elasticsearch data source added earlier.
2. Choose the operation you want to perform on your Elasticsearch cluster.
:::tip
Query results can be transformed using transformations. Refer to our transformations documentation for more details: **[link](/docs/beta/app-builder/custom-code/transform-data)**
:::
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
ToolJet supports the following Elasticsearch operations:
- **[Search](#search)**
- **[Index a Document](#index-a-document)**
- **[Get a Document](#get-a-document)**
- **[Update a Document](#update-a-document)**
- **[Delete a Document](#delete-a-document)**
- **[Bulk Operation](#bulk-operation)**
- **[Count Documents](#count-documents)**
- **[Check Document Existence](#check-document-existence)**
- **[Multi Get](#multi-get)**
- **[Scroll Search](#scroll-search)**
- **[Clear Scroll](#clear-scroll)**
- **[Get Cat Indices](#get-cat-indices)**
- **[Get Cluster Health](#get-cluster-health)**
### Search
This operation executes a search query and returns matching search hits. For more details, see the Elasticsearch search guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html)**.
#### Required Parameter
- **Index**: The name of the index to search in.
- **Query**: The search query in JSON format.
#### Optional Parameter
- **Scroll**: Scroll time.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/elastic-search-v2.png" alt="Elastic search" style={{marginBottom:'15px'}} />
</div>
#### Example:
```yaml
Index: books
Query: { "query": { "match": { "title": "The Great Gatsby" } }, "size": 20 }
Scroll: 1m # Can be in the format of 1m, 1h, 1d.
```
### Index a Document
This operation adds a JSON document to the specified index or data stream. For more details, see the Elasticsearch index guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html)**.
#### Required Parameter
- **Index**: The name of the index to add the document to
- **Body**: The document body in JSON format
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/index-v2.png" alt="Elastic index" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Index: books
Body:
{
"title": "1984",
"author": "George Orwell",
"year": 1949,
"genre": "Dystopian Fiction",
}
```
### Get a Document
This operation retrieves the specified JSON document from the index. For more details, see the Elasticsearch get guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html)**.
#### Required Parameter
- **Index**: The name of the index to get the document from
- **Id**: The ID of the document to retrieve
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/get-v2.png" alt="Elastic get" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Index: books
Id: FJXTSZEBsuzUn2y4wZ-W
```
### Update a Document
This operation updates a document using the specified script. For more details, see the Elasticsearch update guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html)**.
#### Required Parameter
- **Index**: The name of the index containing the document
- **Id**: The ID of the document to update
- **Body**: The update script or partial document in JSON format
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/update-v2.png" alt="Elastic update" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Index: books
Id: FJXTSZEBsuzUn2y4wZ-W
Body:
{
"doc": {
"title": "1984",
"author": "George Orwell",
"year": 1949,
"genre": "Fiction"
}
}
```
### Delete a Document
This operation removes a JSON document from the specified index. For more details, see the Elasticsearch delete guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html)**.
#### Required Parameter
- **Index**: The name of the index containing the document
- **Id**: The ID of the document to delete
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/delete.png" alt="Elastic delete" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Index: books
Id: FJXTSZEBsuzUn2y4wZ-W
```
### Bulk Operation
This operation performs multiple index/update/delete operations in a single API call. For more details, see the Elasticsearch bulk guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html)**.
#### Required Parameter
- **Operations**: The bulk operations to perform in JSON format
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/bulk.png" alt="Elastic bulk" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
[
{ "index": { "_index": "books", "_id": "book1" } },
{
"title": "The Great Gatsby",
"author": "F. Scott Fitzgerald",
"year": 1925,
},
{ "delete": { "_index": "books", "_id": "book2" } },
{ "index": { "_index": "books", "_id": "book3" } },
{ "title": "Moby-Dick", "author": "Herman Melville", "year": 1851 },
{ "delete": { "_index": "books", "_id": "book4" } },
]
```
### Count Documents
This operation returns the number of matches for a search query. For more details, see the Elasticsearch count guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html)**.
#### Required Parameter
- **Index**: The name of the index to count documents in.
#### Optional Parameter
- **Query**: The query to filter documents in JSON format
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/count.png" alt="Elastic count" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
{ "query": { "range": { "timestamp": { "gte": 1901 } } } }
```
### Check Document Existence
This operation checks if a document exists in an index. For more details, see the Elasticsearch exists guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html#docs-get-api-response-codes)**.
#### Required Parameter:
- **Index**: The name of the index to check for document existence
- **Id**: The ID of the document to check
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/exists.png" alt="Elastic exists" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Index: books
Id: FJXTSZEBsuzUn2y4wZ-W
```
### Multi Get
This operation retrieves multiple documents in a single request. For more details, see the Elasticsearch multi-get guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-get.html)**.
#### Required Parameter
- **Operations**: The multi-get operations to perform in JSON format
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/mget.png" alt="Elastic multi get" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
{
"docs":
[
{ "_index": "books", "_id": "book124" },
{ "_index": "books", "_id": "book125" },
],
}
```
### Scroll Search
This operation retrieves large numbers of results from a single search request. For more details, see the Elasticsearch scroll guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#scroll-search-results)**.
#### Required Parameter
- **Scroll ID**: The scroll ID for the search
- **Scroll**: The scroll time
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/scroll-search.png" alt="Elastic scroll" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Scroll ID: DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAOWQWYm9vbDItY1NCOUExal9TcTBjeUEyZw
Scroll: 60m
```
### Clear Scroll
This operation clears the search context for a scroll. For more details, see the Elasticsearch clear scroll guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/clear-scroll-api.html)**.
#### Required Parameter
- **Scroll ID**: The scroll ID to clear
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/clear-scroll.png" alt="Elastic clear scroll" style={{marginBottom:'15px'}}/>
</div>
#### Example:
```yaml
Scroll ID: DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAOWQWYm9vbDItY1NCOUExal9TcTBjeUEyZw
```
### Get Cat Indices
This operation provides a compact, column-aligned view of indices in a cluster. For more details, see the Elasticsearch cat indices guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/cat-indices.html)**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/cat-indices.png" alt="Elastic cat indices" />
</div>
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"body": [
{
"health": "yellow",
"status": "open",
"index": "1",
"uuid": "JQOzqxK7Rdar7ROOlqXwkA",
"pri": "1",
"rep": "1",
"docs.count": "2",
"docs.deleted": "0",
"store.size": "9.2kb",
"pri.store.size": "9.2kb"
},
{
"health": "yellow",
"status": "open",
"index": "recipes",
"uuid": "eNGdAsG4TMWvs9f0eLERlQ",
"pri": "1",
"rep": "1",
"docs.count": "20",
"docs.deleted": "0",
"store.size": "30kb",
"pri.store.size": "30kb"
},
{
"health": "yellow",
"status": "open",
"index": "read_me",
"uuid": "EbE4V-5RRE2y-_P4z_auVQ",
"pri": "1",
"rep": "1",
"docs.count": "1",
"docs.deleted": "0",
"store.size": "5.1kb",
"pri.store.size": "5.1kb"
}
],
"statusCode": 200,
"headers": {
"x-elastic-product": "Elasticsearch",
"content-type": "application/json",
"content-length": "558"
},
"meta": {
"context": null,
"request": {
"params": {
"method": "GET",
"path": "/_cat/indices",
"body": null,
"querystring": "format=json",
"headers": {
"user-agent": "opensearch-js/1.2.0 (linux 6.5.0-1021-aws-x64; Node.js v18.18.2)"
},
"timeout": 30000
},
"options": {},
"id": 1
},
"name": "opensearch-js",
"connection": {
"url": "http://xx.2xx.183.199:9200/",
"id": "http://xx.2xx.183.199:9200/",
"headers": {},
"deadCount": 0,
"resurrectTimeout": 0,
"_openRequests": 0,
"status": "alive",
"roles": {
"master": true,
"data": true,
"ingest": true
}
},
"attempts": 0,
"aborted": false
}
}
```
</details>
### Get Cluster Health
This operation retrieves the status of the clusters health. For more details, see the Elasticsearch cluster health guide **[here](https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-health.html)**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/elasticsearch/cluster-health.png" alt="Elastic cluster health" />
</div>
<details id="tj-dropdown">
<summary>**Response Example**</summary>
```json
{
"body": {
"cluster_name": "docker-cluster",
"status": "yellow",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 10,
"active_shards": 10,
"relocating_shards": 0,
"initializing_shards": 0,
"unassigned_shards": 3,
"delayed_unassigned_shards": 0,
"number_of_pending_tasks": 0,
"number_of_in_flight_fetch": 0,
"task_max_waiting_in_queue_millis": 0,
"active_shards_percent_as_number": 76.92307692307693
},
"statusCode": 200,
"headers": {
"x-elastic-product": "Elasticsearch",
"content-type": "application/json",
"content-length": "405"
},
"meta": {
"context": null,
"request": {
"params": {
"method": "GET",
"path": "/_cluster/health",
"body": null,
"querystring": "",
"headers": {
"user-agent": "opensearch-js/1.2.0 (linux 6.5.0-1021-aws-x64; Node.js v18.18.2)"
},
"timeout": 30000
},
"options": {},
"id": 1
},
"name": "opensearch-js",
"connection": {
"url": "http://xx.2xx.183.199:9200/",
"id": "http://xx.2xx.183.199:9200/",
"headers": {},
"deadCount": 0,
"resurrectTimeout": 0,
"_openRequests": 0,
"status": "alive",
"roles": {
"master": true,
"data": true,
"ingest": true
}
},
"attempts": 0,
"aborted": false
}
}
```
</details>
</div>

View file

@ -0,0 +1,175 @@
---
id: firestore
title: Cloud Firestore
---
ToolJet can connect to **Cloud Firestore** databases to read and write data.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the **Cloud Firestore** 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 Cloud Firestore as the data source.
ToolJet requires the following to connect to your BigQuery:
- **Private key**
For generating a private key check out **[Firestore's official documentation](https://cloud.google.com/iam/docs/creating-managing-service-account-keys#iam-service-account-keys-create-console)**.
<img className="screenshot-full" src="/img/datasource-reference/firestore/add-ds-firestore-v2.png" alt="firestore add ds"/>
</div>
<div style={{paddingTop:'24px'}}>
## Querying Firestore
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **Cloud Firestore** datasource added in previous step.
3. Select the desired operation from the dropdown and enter the required parameters.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to trigger the query.
<img className="screenshot-full" src="/img/datasource-reference/firestore/firestore-query-v2.png" alt="firestore QUERY" />
:::tip
Query results can be transformed using transformations. Read our transformations documentation to see how: **[link](/docs/beta/app-builder/custom-code/transform-data)**
:::
</div>
<div style={{paddingTop:'24px'}}>
## Supported Operations
- **[Get Document](#get-document)**
- **[Query collection](#query-collection)**
- **[Add Document to Collection](#add-document-to-collection)**
- **[Update Document](#update-document)**
- **[Set Document](#set-document)**
- **[Bulk update using document ID](#bulk-update-using-document-id)**
- **[Delete Document](#delete-document)**
### Get Document
Use this operation to get the data in a document.
#### Required Parameters
- **Path**
<img className="screenshot-full" src="/img/datasource-reference/firestore/get-v2.png" alt="firestore get" style={{marginBottom:'15px'}}/>
### Query Collection
Use this operation to query all the documents in a collection. Check firestore doc **[here](https://firebase.google.com/docs/reference/js/v8/firebase.database.Query)**.
#### Required Parameters
- **Path**
#### Optional parameters
- **Order type**
- **Limit**
- **Field**
- **Operator**
- **Value**
<img className="screenshot-full" src="/img/datasource-reference/firestore/query-collection-v2.png" alt="firestore collection" style={{marginBottom:'15px'}}/>
### Add Document to Collection
Use this operation for creating a new document in a collection.
#### Required Parameters
- **Collection**
- **Body**.
#### Example
```json
{
"Author": "Shubh",
"id": 5
}
```
<img style={{marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/firestore/add-document-v2.png" alt="firestore document" />
### Update Document
Use this operation for updating the existing document in a collection. Also, it only updates fields if they exist, but doesn't replace an entire object like **[set operation](#set-document)**.
#### Required Parameters
- **Path**
- **Body**
#### Example
```json
{
"Author": "Shubhendra",
"id": 3
}
```
<img style={{ marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/firestore/update-v2.png" alt="firestore update" />
### Set Document
This operation replaces your chosen object with the value that you provide. So if your object has 5 fields, and you use Set operation and pass object with 3 fields, it will now have 3 fields.
#### Required Parameters
- **Path**
- **Body**
#### Example
```json
{
"Author": "Shefewfbh",
"id": 9
}
```
<img style={{ marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/firestore/set-v2.png" alt="firestore set" />
### Bulk Update Using Document ID
Use this operation for bulk updating documents.
#### Required Parameters
- **Collection**
- **Key for document ID**
- **Records**
<img style={{ marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/firestore/bulk-v2.png" alt="firestore bulk" />
### Delete Document
Use this operation for deleting a document in a collection.
#### Required Parameters
- **Path**
<img style={{ marginBottom:'15px' }} className="screenshot-full" src="/img/datasource-reference/firestore/delete-v2.png" alt="firestore delete"/>
</div>
<div style={{paddingTop:'24px', paddingBottom:'24px'}}>
## Transforming Firestore Query Result for Table Widget
The Firestore query result is in the form of object so well need to transform it into array.
```js
return (data = Array(data));
```
</div>

View file

@ -0,0 +1,127 @@
---
id: gcs
title: Google Cloud Storage
---
ToolJet can connect to GCS buckets and perform various operation on them.
<div style={{paddingTop:'24px'}}>
## Connection
To establish a connection with the Google Cloud Storage 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 through the ToolJet dashboard.
ToolJet requires the following to connect to a GCS datasource:
- **JSON Private Key**
You can follow the [google documentation](https://cloud.google.com/docs/authentication/getting-started) to get started.
<img className="screenshot-full" src="/img/datasource-reference/gcs/gcs-connect-v2.png" alt="gcs connection" />
</div>
<div style={{paddingTop:'24px'}}>
## Querying GCS
1. Click on **+ Add** button of the query manager at the bottom panel of the editor.
2. Select the **GCS** datasource added in previous step.
3. Select the Operation.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to create and trigger the query.
:::tip
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/beta/app-builder/custom-code/transform-data)
:::
#### Supported operations
- **[Read file](#read-file)**
- **[Upload file](#uplodad-file)**
- **[List buckets](#list-buckets)**
- **[List files in a bucket](#list-files-in-a-bucket)**
- **[Signed url for download](#signed-url-for-download)**
- **[Signed url for upload](#signed-url-for-upload)**
<img className="screenshot-full" src="/img/datasource-reference/gcs/gcs-query-v2.png" alt="gcs query" style={{marginBottom:'15px'}} />
### Read File
Reads the content of a file from GCS.
#### Required Parameter
- **Bucket**
- **File Name**
<img className="screenshot-full" src="/img/datasource-reference/gcs/readFile.png" alt="gcs query" style={{marginBottom:'15px'}} />
### Uplodad File
Uploads a file to GCS.
#### Required Parameter
- **Bucket**
- **File name**
- **Upload data**
#### Optional Parameter
- **Content Type**
- **Encoding**
<img className="screenshot-full" src="/img/datasource-reference/gcs/uploadFile.png" alt="gcs query" style={{marginBottom:'15px'}} />
### List Buckets
Retrieves a list of available buckets.
<img className="screenshot-full" src="/img/datasource-reference/gcs/listBuckets.png" alt="gcs query" style={{marginBottom:'15px'}} />
### List Files in a Bucket
Lists files within a specific GCS bucket.
#### Required Parameter
- **Bucket**
#### Optional Parameter
- **Prefix**
<img className="screenshot-full" src="/img/datasource-reference/gcs/listFiles.png" alt="gcs query" style={{marginBottom:'15px'}} />
### Signed URL for Download
Generates a signed URL for downloading a file.
#### Required Parameter
- **Bucket**
- **File Name**
#### Optional Parameter
- **Expires in**
<img className="screenshot-full" src="/img/datasource-reference/gcs/urlDownload.png" alt="gcs query" style={{marginBottom:'15px'}} />
### Signed URL for Upload
Generates a signed URL for uploading a file.
#### Required Parameter
- **Bucket**
- **File name**
#### Optional Parameter
- **Expires in**
- **Content Type**
<img className="screenshot-full" src="/img/datasource-reference/gcs/urlUpload.png" alt="gcs query" style={{marginBottom:'15px'}} />
</div>

View file

@ -0,0 +1,171 @@
---
id: googlesheets
title: Google Sheets
---
ToolJet has the capability to establish a connection with Google Sheet for both reading and writing data. By utilizing OAuth 2.0, ToolJet can establish a secure connection with Google Sheet, ensuring that the application's access to a user's account is restricted and limited appropriately.
## Self-Hosted Configuration
If you decide to self-host ToolJet, there are a few additional steps you need to take:
1. Proceed with the setup steps provided in the [Google OAuth 2.0 guide](/docs/setup/env-vars#google-oauth) to configure the necessary settings.
2. Assign the corresponding values obtained from the previous step to the following environment variables:
- **GOOGLE_CLIENT_ID**
- **GOOGLE_CLIENT_SECRET**
- **TOOLJET_HOST**
3. Activate the Google Sheets API within the Google Cloud Platform (GCP) console.
## Connection
To establish a connection with the Google Sheet 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 through the ToolJet dashboard.
### Authorization Scopes
When connecting to a Google Sheets data source, you can choose between two permission scopes:
1. **Read Only**: This scope allows you to access and retrieve data from the Google Sheet.
2. **Read and Write**: This scope grants you both read and write permissions, enabling you to retrieve and modify data within the Google Sheet.
<img className="screenshot-full img-l" src="/img/datasource-reference/google-sheets/sheetconnect-v3.png" alt="Google Sheet" />
## Querying Google Sheet
1. Click the **+ Add** button in the query manager located at the bottom panel of the editor.
2. Select the **Google Sheet** data source under the data source section.
3. Choose the desired operation from the dropdown.
4. Click on the **Preview** button to preview the output or Click on the **Run** button to create and trigger the query.
Using Google sheets data source you can perform several operations from your applications like:
1. **[Create a spreadsheet](#create-a-spreadsheet)**
2. **[List all sheets of a spreadsheet](#list-all-sheets-of-a-spreadsheet)**
3. **[Read data from a spreadsheet](#read-data-from-a-spreadsheet)**
4. **[Append data to a spreadsheet](#append-data-to-a-spreadsheet)**
5. **[Get spreadsheet info](#get-spreadsheet-info)**
6. **[Update single row of a spreadsheet](#update-single-row-of-a-spreadsheet)**
7. **[Delete row from a spreadsheet](#delete-row-from-a-spreadsheet)**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/operations-v4.png" alt="Google Sheet Operations" style={{marginBottom:'15px'}} />
:::info
**Spreadsheet ID** can be obtained from the URL of the spreadsheet. For example, in the URL `https://docs.google.com/spreadsheets/d/1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM/edit#gid=0`, the `1W2S4re7zNaPk9vqv6_CqOpPdm_mDEqmLmzjVe7Nb9WM` represents the spreadsheet ID.
:::
### Create a Spreadsheet
This operation can be used to create a new spreadsheet.
#### Required Parameter
- **Title**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/create-sheet-v2.png" alt="create a spreadsheet" />
### List All Sheets of a Spreadsheet
This operation can be used to list all sheets of a spreadsheet.
#### Required Parameter
- **Spreadsheet ID**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/list-all-sheets-v2.png" alt="create a spreadsheet" style={{marginBottom:'15px'}} />
### Read Data From a Spreadsheet
This operation allows you to retrieve the table data from a spreadsheet in the form of a JSON object.
#### Required Parameter
- **Spreadsheet ID**
#### Optional Parameter
- **Range**
- **Sheet**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/read-data-op-v3.png" alt="Google Sheet Operations" style={{marginBottom:'15px'}} />
### Append Data to a Spreadsheet
Add additional rows to a table by using the append operation.
#### Required Parameter
- **Spreadsheet ID**
- **Rows**
#### Optional Parameter
- **Sheet**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/append-data-op-v3.png" alt="Google Sheet Operations" style={{marginBottom:'15px'}} />
#### Example
```yaml
[
{
"name": "John",
"email": "john@tooljet.com",
"date": "2024-09-16",
"status": "Confirmed",
"phone": "+123456789"
},
{
"name": "Jane",
"email": "jane@tooljet.com",
"date": "2024-09-17",
"status": "Pending",
"phone": "+987654321"
},
{
"name": "Doe",
"email": "doe@tooljet.com",
"date": "2024-09-18",
"status": "Cancelled",
"phone": "+112233445"
}
]
```
### Get Spreadsheet Info
This operation allows you to retrieve basic information about the spreadsheet, including the number of sheets, theme, time zone, format, and URL, among others.
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/info-v3.png" alt="google sheets get info" style={{marginBottom:'15px'}} />
### Update Single Row of a Spreadsheet
This operation allows you to update existing data in a sheet.
#### Required Parameters
- **Spreadsheet ID**
- **Where**
- **Operator**
- **Value**
- **Body**
#### Optional Parameters
- **Range**
- **Sheet**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/upd-v2.png" alt="Google Sheet Operations" />
#### Example
```yaml
{
"name": "Hugo Lefevre",
"position": "Product Manager",
"url": "https://abctech.com/hugo",
"date-applied": "2024-09-10",
"status": "Application Under Review"
}
```
### Delete Row From a Spreadsheet
This operation allows you to delete a specific row from the sheet.
#### Required Parameter
- **Spreadsheet ID**
- **Delete row number**
#### Optional Parameter
- **GID**
<img className="screenshot-full img-full" src="/img/datasource-reference/google-sheets/del-v3.png" alt="google sheets delete" style={{marginBottom:'15px'}} />

Some files were not shown because too many files have changed in this diff Show more