Skip to content
Back to skills

Appsmith

ASecurity

Appsmith is an open-source low-code platform for building internal tools, admin panels, and dashboards. Use when a user asks to create CRUD interfaces, connect to databases or APIs with drag-and-drop widgets, write JSObjects for business logic, or self-host Appsmith with Docker or Kubernetes.

  • 142 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
developmentjavascriptgojavabashsqldockerkubernetesgitapidatabase

Works with

  • terminal
  • cli
  • api
  • mcp

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill appsmith --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Appsmith?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Appsmith
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-appsmith/badge)](https://www.skillsdirectory.com/skills/terminalskills-appsmith)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: appsmith
description: >-
  Appsmith is an open-source low-code platform for building internal tools, admin panels, and dashboards. Use when a user asks to create CRUD interfaces, connect to databases or APIs with drag-and-drop widgets, write JSObjects for business logic, or self-host Appsmith with Docker or Kubernetes.
license: Apache-2.0
compatibility: "Self-hosting: Docker 20.10.7+ and about 8 GB RAM, or Kubernetes 1.33+ with Helm 3.14+. Appsmith Cloud needs only a browser."
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: development
  repository: https://github.com/appsmithorg/appsmith
  tags: ["internal-tools", "low-code", "open-source", "admin-panel", "self-hosted"]
---
# Appsmith — Open-Source Internal Tool Builder

## Overview

Appsmith is an open-source low-code platform for internal tools: widgets are dragged onto a canvas, queries talk to databases and APIs, and JavaScript (`{{ }}` bindings and JSObjects) ties them together. It runs on Appsmith Cloud or self-hosted as a single container that bundles the server, MongoDB, Redis and PostgreSQL. Two images exist: `appsmith-ce` (Community, Apache-2.0) and `appsmith-ee` (Commercial edition with a free plan; Business and Enterprise features unlock with a license key). This skill reflects Appsmith v2.4 (September 2026).

## Instructions

### Data Queries

```sql
-- OrdersQuery (PostgreSQL). Bindings read widget values; with prepared statements on, each one is a parameter.
-- Never put a binding inside a SQL comment: it is still counted as a parameter.
SELECT * FROM orders
WHERE status = {{ StatusSelect.selectedOptionValue }}
  AND created_at BETWEEN {{ FromDate.selectedDate }} AND {{ ToDate.selectedDate }}
  AND ({{ SearchInput.text }} = '' OR customer_email ILIKE '%' || {{ SearchInput.text }} || '%')
ORDER BY created_at DESC
LIMIT {{ OrdersTable.pageSize }} OFFSET {{ OrdersTable.pageOffset }}

-- UpdateOrderQuery: values passed at call time arrive as this.params
UPDATE orders SET status = {{ this.params.status }} WHERE id = {{ this.params.orderId }}
```

```
UpdatePlanAPI (REST API datasource; auth headers live in the datasource)
PUT https://api.northwind.io/users/{{ UsersTable.selectedRow.id }}
Body: { "plan": {{ PlanSelect.selectedOptionValue }}, "note": {{ NoteInput.text }} }
```

Widget properties used most: Table `selectedRow`, `pageNo`, `pageSize`, `pageOffset`, `searchText`; Select `selectedOptionValue`; Input `text`; DatePicker `selectedDate`. For server-side pagination turn on the table's **Server side pagination** and run the query from **onPageChange**.

Each query has a **Run behavior** in its settings: *Manual* (only `.run()` or a widget event), *On page load*, or *Automatic* (re-runs whenever a bound value changes). Do not combine Automatic with an event handler that also calls `.run()` — the query executes twice. Other settings there: **Use Prepared Statements** (on by default), **Request confirmation before running**, and **Query timeout** (default 10 000 ms, maximum 60 000 ms).

### JavaScript Objects (JSObjects)

```javascript
// JSObject "Refunds" — reusable business logic
export default {
  // Transform query data for charts (Lodash `_` and `moment` are built in)
  getRevenueByMonth() {
    return OrdersQuery.data.reduce((acc, order) => {
      const month = moment(order.created_at).format("YYYY-MM");
      acc[month] = (acc[month] || 0) + order.amount;
      return acc;
    }, {});
  },

  // Multi-step action, run from the Confirm button inside ConfirmRefundModal
  async processRefund() {
    const order = OrdersTable.selectedRow;       // {} when nothing is selected
    if (!order.id) {
      showAlert("Select an order first", "warning");
      return;
    }
    try {
      await StripeRefundAPI.run({ chargeId: order.stripe_charge_id });        // {{ this.params.chargeId }}
      await UpdateOrderQuery.run({ orderId: order.id, status: "refunded" });
      await SlackNotifyAPI.run({ message: `Refund processed for order #${order.id} ($${order.amount})` });
      showAlert("Refund processed successfully", "success");
      await OrdersQuery.run();                   // refresh the table
    } catch (error) {
      showAlert(`Refund failed: ${error.message}`, "error");
    } finally {
      closeModal(ConfirmRefundModal.name);
    }
  },

  // Form validation
  validateForm() {
    const errors = {};
    if (!EmailInput.text?.includes("@")) errors.email = "Invalid email";
    if (Number(AmountInput.text) <= 0) errors.amount = "Amount must be positive";
    if (!ReasonSelect.selectedOptionValue) errors.reason = "Select a reason";
    return errors;
  },
};
```

Framework functions: `Query.run(params)` returns a promise with the response; `showAlert(message, type)` shows a 5-second toast (`info`, `success`, `warning`, `error`); `showModal(Modal.name)` and `closeModal(Modal.name)` open and close a Modal widget; `storeValue(key, value, persist)` writes to `appsmith.store`. `showModal` only opens the modal — it does not wait for an answer, so put the confirmed action on a button inside the modal (or use the query's confirmation setting).

### Deployment

```yaml
# ~/appsmith/docker-compose.yml — pin a release tag from github.com/appsmithorg/appsmith/releases
services:
  appsmith:
    image: index.docker.io/appsmith/appsmith-ee:v2.4.3   # or appsmith/appsmith-ce:v2.4.3
    container_name: appsmith
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./stacks:/appsmith-stacks
    restart: unless-stopped
```

```bash
docker compose up -d               # first start takes up to 5 minutes; then open http://localhost
docker compose exec -it appsmith appsmithctl backup     # encrypted archive in ./stacks/data/backup/
# settings: edit ./stacks/configuration/docker.env, then
docker compose restart appsmith

# Kubernetes with Helm (Commercial edition chart; the Community chart is at https://helm.appsmith.com)
helm repo add appsmith-ee https://helm-ee.appsmith.com && helm repo update
helm install appsmith-ee appsmith-ee/appsmith -n appsmith-ee --create-namespace -f values.yaml
kubectl get pods -n appsmith-ee
```

`values.yaml` must set `image.tag` and the ingress `className` and host; the Kubernetes guide also sets `mongodb.enabled: false` and enables `mongodbCommunity` and `mongodbOperator`, replacing the legacy Bitnami MongoDB subchart.

### Git Sync

1. Open the app and click **Connect Git** at the bottom left of the editor. Only SSH remotes work (HTTPS Git URLs are not supported), and the target repository must be empty.
2. Paste the SSH URL, generate a key (ECDSA 256 or RSA 4096), and add it to the repository as a deploy key **with write access**.
3. Work on a feature branch, commit and push from Appsmith, open a pull request, merge, then pull in Appsmith and deploy.

Branch protection, a per-instance default branch and automatic pull-and-deploy after a merge (a deploy URL called by CI with a bearer token) are Enterprise features in Git settings; without them someone pulls the branch in Appsmith by hand.

## Examples

### Example 1: Self-host Appsmith for a team

User: "Set up Appsmith on our Ubuntu server with Docker and make sure we can back it up."

```bash
mkdir -p ~/appsmith && cd ~/appsmith
# write the docker-compose.yml shown above with image tag v2.4.3
docker compose up -d
docker compose ps                  # the appsmith container should be listed as running
until curl -fsS http://localhost/api/v1/health >/dev/null; do sleep 10; done   # server is up (up to 5 minutes)
docker compose exec -it appsmith appsmithctl backup
ls stacks/data/backup/             # appsmith-backup-*.tar.gz.enc
```

After a few minutes the server's address (`http://localhost` on the same machine) shows the setup page where the first visitor creates the administrator account, so open it right away. The backup prompts for a password and cannot be restored without it. To set a custom domain or disable telemetry, edit `stacks/configuration/docker.env` (`APPSMITH_CUSTOM_DOMAIN`, `APPSMITH_DISABLE_TELEMETRY=true`) and restart.

### Example 2: Orders admin panel with a guarded refund

User: "Build an orders page: filter by status, search by email, and let support refund the selected order after confirming."

1. Add the filter widgets `StatusSelect`, `FromDate`, `ToDate` and `SearchInput`, then a Table `OrdersTable` with data `{{ OrdersQuery.data }}` and server-side pagination on.
2. Add a PostgreSQL datasource and create `OrdersQuery` and `UpdateOrderQuery` from the Data Queries section. Set `OrdersQuery` to Automatic so it re-runs when a filter or the table page changes — do not also call it from onPageChange. Create the REST queries `StripeRefundAPI` and `SlackNotifyAPI` (reading `{{ this.params.chargeId }}` and `{{ this.params.message }}`) and the `Refunds` JSObject from above.
3. Add a "Refund" button with onClick `{{ showModal(ConfirmRefundModal.name) }}` and Disabled `{{ !OrdersTable.selectedRow.id || OrdersTable.selectedRow.status === 'refunded' }}`.
4. Add a Modal named `ConfirmRefundModal` and set its Confirm button's onClick to `{{ Refunds.processRefund() }}`.

Selecting order 4812 and confirming runs the three queries in order, shows the toast "Refund processed successfully", reloads the table with the row's status now `refunded` and closes the modal. If Stripe rejects the refund, the toast reads "Refund failed: …", the order is not updated and the modal closes.

## Guidelines

1. **Pin the image tag** — without a tag Docker pulls `latest` and a container recreate becomes an unplanned upgrade.
2. **Upgrade through the checkpoint** — an instance older than v1.96 must first be upgraded to a v1.96–v1.99 release before moving to v2.x (v2 bundles MongoDB 7); instances older than v1.9.2 have an earlier checkpoint. Run `appsmithctl backup` first.
3. **Keep prepared statements on** — they are the SQL-injection defence. A binding is then a value, not SQL text, so it cannot supply a table or column name; build patterns with `'%' || {{ SearchInput.text }} || '%'`.
4. **JSObjects for logic** — keep multi-step logic in JSObjects rather than long inline `{{ }}` handlers; use `async/await` with `try/catch` so a failed query stops the chain.
5. **`selectedRow` is never null** — it is `{}` when nothing is selected, so test a field (`selectedRow.id`), not the object.
6. **Mind the 10-second query timeout** — raise it per query (up to 60 s); self-hosted instances can lift that 60 s ceiling with `APPSMITH_SERVER_TIMEOUT` (in seconds). Move longer work out of the request.
7. **Credentials stay in datasources** — never paste API keys into queries or JSObjects, where every app editor (and the Git repository) can read them.
8. **Paid features** — multiple datasource environments, granular access control, audit logs, reusable Packages and Workflows need the Business plan; Git CI/CD needs Enterprise; the MCP server (beta) is for Business Cloud and self-hosted Enterprise.
9. **Appsmith AI is gone** — the Appsmith AI datasource reached end of life on September 30, 2026; use an OpenAI, Anthropic or Google AI datasource instead.
10. **Network needs** — self-hosted instances call `cs.appsmith.com`; allow it in outbound firewall rules.

Files in this skill

  • SKILL.md5 KB
  • _scores.json1.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…