Back

How to Build a Simple CRUD App in Appsmith

How to Build a Simple CRUD App in Appsmith

Building internal tools from scratch wastes time you don’t have. You need a quick admin panel or data management interface, not a three-month development project. Appsmith lets you connect a datasource, drag in widgets, and wire up Create, Read, Update, and Delete operations in under an hour.

This tutorial walks through the standard Appsmith CRUD app workflow: connecting your database, displaying data in a Table widget, handling record creation and updates via forms, and refreshing data after mutations.

Key Takeaways

  • Connect SQL databases or REST APIs to Appsmith using encrypted, environment-specific datasource credentials
  • Bind query results to Table widgets using the {{queryName.data}} syntax
  • Use Modals with Input widgets to handle both record creation and updates
  • Distinguish between triggeredRow (button click context) and selectedRow (highlighted row) for accurate data operations
  • Refresh table data after every mutation by calling queryName.run() on success

Connect Your Datasource

Start by creating a new application in Appsmith. From the left sidebar, click the + next to Datasources and select your database type—PostgreSQL, MySQL, or a REST API.

For SQL databases, enter your connection credentials: host, port, database name, username, and password. Store sensitive values in Appsmith’s encrypted datasource configuration and environment settings rather than hardcoding them in queries or widgets. Queries are executed server-side, so credentials are never exposed to the browser.

You can find the full list of supported databases and configuration options in the official datasource documentation.

Test the connection before saving. If it fails, check firewall rules, SSL requirements, and credential accuracy.

Read Data with a Query

Create your first query to fetch records. Click + next to Queries/JS, select your datasource, and write a SELECT statement:

SELECT id, name, email, created_at FROM users ORDER BY created_at DESC LIMIT 100;

Name this query getUsers and run it to verify the response.

For larger datasets, implement server-side pagination. Appsmith’s Table widget supports this natively—you’ll pass offset and limit parameters to your query based on the table’s pagination state.

Display Data in a Table Widget

Drag a Table widget onto the canvas. In the widget properties, set the Table data property to:

{{getUsers.data}}

The table automatically generates columns from your query response. Rename columns, adjust widths, and hide fields like internal IDs as needed.

This Appsmith database integration pattern—query to widget binding—forms the foundation of every low-code CRUD frontend you’ll build. The Table widget’s full configuration options are covered in the Table widget documentation.

Create Records with a Form or Modal

Add a Button widget labeled “Add New” and set its onClick action to open a Modal. Inside the modal, add Input widgets for each field: nameInput, emailInput.

Create an INSERT query named createUser:

INSERT INTO users (name, email) VALUES ({{nameInput.text}}, {{emailInput.text}});

Wire the modal’s submit button to run createUser. On success, trigger getUsers.run() to refresh the table, then close the modal.

For validation, use the Input widget’s built-in validation properties or write a JSObject that checks field values before submission.

Update Records Using Selected Rows

Enable row selection on your Table widget. When a user clicks a row, Table1.selectedRow exposes that record’s data.

Create an UPDATE query named updateUser:

UPDATE users
SET name = {{nameInput.text}}, email = {{emailInput.text}}
WHERE id = {{Table1.selectedRow.id}};

You can reuse the same modal for editing by pre-populating inputs with Table1.selectedRow.name as default values. Toggle between “create” and “edit” modes using a JSObject variable.

After the update runs successfully, call getUsers.run() to refresh the display.

Delete Records with Confirmation

Add a Button column to your table for delete actions. Set the column type to Button and configure the onClick to run a DELETE query:

DELETE FROM users WHERE id = {{Table1.triggeredRow.id}};

Note the difference: triggeredRow captures the row where the button was clicked, while selectedRow captures the highlighted row.

Enable Request confirmation before running in the query settings to prevent accidental deletions. On success, refresh the table data.

The Appsmith Table Form Workflow

The pattern you’ve built—Table widget displaying query results, Form or Modal handling input, parameterized queries for mutations, and data refresh on success—scales to any CRUD use case. This Appsmith table form workflow applies whether you’re managing users, inventory, support tickets, or any other records.

For production apps, consider:

  • Role-based access: Appsmith supports granular permissions at the application, page, and datasource level
  • Environments and credentials: Store database passwords and API keys securely across dev, staging, and production
  • Packages: Share reusable queries and JSObjects across multiple applications using Appsmith Packages
  • Server-side pagination: Essential for tables with thousands of records

Conclusion

You now have a functional CRUD interface: data displayed in a table, forms for creating and editing records, delete functionality with confirmation, and automatic data refresh after every mutation.

From here, extend the app with search filters, additional pages, or connect to Appsmith Workflows for backend automation. The low-code CRUD frontend you’ve built serves as the foundation for more complex internal tools—without writing the UI and data layer from scratch.

FAQs

selectedRow refers to the row a user has highlighted by clicking on it, while triggeredRow refers to the specific row where a button or action was clicked. Use triggeredRow for button columns in tables to ensure the action targets the correct record, and selectedRow when you want to reference the currently highlighted row for editing or display purposes.

Appsmith supports parameterized queries for many SQL datasources when you use mustache bindings like {{inputWidget.text}}, but the exact behavior depends on the database driver. Avoid manual string concatenation in SQL, validate input using widget validation properties, and apply server-side constraints where possible.

Yes. Create a JSObject variable to track the current mode, such as formMode set to either create or edit. When opening the modal for a new record, set formMode to create and clear input fields. When editing, set formMode to edit and populate inputs with Table1.selectedRow values. Your submit button can then conditionally run either createUser or updateUser based on the mode.

Enable server-side pagination in your Table widget properties. Then modify your query to accept pagination values from the table widget (page size and offset) and apply them in your SQL using LIMIT and OFFSET. The table automatically handles page navigation and triggers query reruns when users change pages.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay