Skip to content

Developers · Data

Manage Database Items with AJAX (CRUD)

Last updated 11 November 2026

This guide demonstrates how you can create, list, update, and delete database items (Records) using JavaScript and GraphQL.

Important

The actions outlined in this document can be performed using the Instance Admin. For more details, please visit the Managing Database Items documentation.

Prerequisites

To follow the steps in this tutorial, you must understand how to use GraphQL as well as interact with the Fetch API.

Steps

Managing Records using AJAX by implementing the full CRUD cycle is a five-step process:

  • Step 1: Define prerequisites: Table, Form, Page, and javascript functions used in multiple places
  • Step 2: Implement the 'Create' operation
  • Step 3: Implement the 'Read' operation
  • Step 4: Implement the 'Update' operation
  • Step 5: Implement the 'Delete' operation

Step 1: Set up the table, form, page and shared JavaScript functions

First, create the necessary database table, form, page, and javascript functions.

Create the table:

app/schema/feedback.yml

name: feedback
properties:
- name: message
  type: string
- name: rate
  type: string

Then create the form object:

app/forms/feedback.liquid

---
name: feedback_form
resource: feedback
resource_owner: anyone
fields:
  properties:
    rate:
      validation: { presence: true }
    message:
      validation: { presence: false }
---
{% include 'create' %}
{% include 'read' %}
{% include 'update' %}
{% include 'delete' %}

Important

For example purposes is set to . This means you can edit any record on a live example page. In real-world scenarios, please configure Permissions to prevent users who are not logged in from submitting a form.

Lastly, create the javascript file:

app/assets/Feedback.js

const request = ({ url, form }) => {
  fetch(url, {
    credentials: 'same-origin', // make sure safari understands what we are doing
    method: 'POST',
    body: new FormData(form) // create FormData from form passed or empty if undefined
  })
};

[...]

Step 2: Implement the 'Create' operation

To implement a 'Create' operation, start by creating a simple form that works without JavaScript:

app/views/partials/create.liquid

{% form html-data-form: 'create' %}
  {% assign p = form.fields.properties %}

  <h2>Create new feedback (record)</h2>
  <label>
    <input type='radio' name='{{ p.rate.name }}' value='Excellent'>
    Excellent
  </label>
  <label>
    <input type='radio' name='{{ p.rate.name }}' value='Meh'>
    Meh
  </label>
  <label>
    <input type='radio' name='{{ p.rate.name }}' value='Very bad'>
    Very bad
  </label>

  <label for='create_message'>Message</label>
  <textarea id='create_message' name='{{ p.message.name }}'></textarea>

  <button>Create</button>
{% endform %}

Then, add JavaScript to make it work in the background:

app/assets/Feedback.js

[...]

const Create = event => {
  event.preventDefault();

  request({
    url: event.target.getAttribute('action'),
    form: event.target
  })
};

const createForm = document.querySelector('[data-form=create]');
createForm.addEventListener('submit', Create);

Step 3: Implement the 'Read' operation

To implement a 'Read' operation, first create a GraphQL query to pull out the data. The query takes the table name in the variable and returns all fields of each record in :

app/graphql/feedback.graphql

query feedback($table: String!, $per_page: Int = 10) {
  records(
    filter: {
      table: { value: $table }
    },
    per_page: $per_page,
    sort: { created_at: { order: DESC } }
  ) {
    results {
      id
      created_at
      updated_at
      properties
    }
  }
}

Then you need to create a page that returns JSON with the data:

app/views/pages/feedback_list.json.liquid

{%- graphql recent = 'feedback', table: 'feedback', per_page: 10 -%}
{{ recent.records.results | json }}

You also need some HTML that will contain the data and a button that will trigger data download:

app/views/partials/read.liquid

<button type='button' data-button='refreshRead'>Refresh content from the server</button>
<table>
  <thead>
    <th>ID</th>
    <th>Created at</th>
    <th>Updated at</th>
    <th>Rating</th>
    <th>Message</th>
  </thead>
  <tbody data-body='readTable'></tbody>
</table>

You also need JavaScript to get the data from the server and render it into the HTML:

app/assets/Feedback.js

const updateReadTable = data => {
  const readBody = document.querySelector('[data-body=readTable]');
  const html = data.map(
    feedback => `<tr>
      <td>${feedback.id}</td>
      <td>${feedback.created_at}</td>
      <td>${feedback.updated_at}</td>
      <td>${feedback.properties.rate}</td>
      <td>${escape(feedback.properties.message)}</td>
    </tr>`
  ).join('');
  readBody.innerHTML = html;
};

const Read = () => {
  fetch('/feedback_list.json')
    .then(response => response.json())
    .then(updateReadTable)
};

const refreshReadButton = document.querySelector('[data-button=refreshRead]');
refreshReadButton.addEventListener('click', Read);

Step 4: Implement the 'Update' operation

Implementing an 'Update' operation is similar to the 'Create' operation, with a couple of differences:

  1. Add a hidden input with the set to PUT
  2. Send the ID of the Database Item you want to edit

app/views/partials/update.liquid

{% form html-data-form: 'update' %}
  {% comment %} Set method using hidden input to tell the server we are updating {% endcomment %}
  <input type='hidden' name='_method' value='PUT' />

  {% comment %} Let user decide which record to edit {% endcomment %}
  <label for='record_id'>Record ID</label>
  <input type='text' name='record_id' value='' required>

  <label>
    <input type='radio' name='{{ p.rate.name }}' value='Excellent'>
    Excellent
  </label>
  <label>
    <input type='radio' name='{{ p.rate.name }}' value='Meh'>
    Meh
  </label>
  <label>
    <input type='radio' name='{{ p.rate.name }}' value='Very bad'>
    Very bad
  </label>

  <label for='update_message'>Message</label>
  <textarea type='text' id='update_message' name='{{ p.message.name }}'></textarea>

  <button>Update</button>
{% endform %}

JavaScript is also very similar, but it takes ID from the input defined above and puts it into the URL.

app/assets/Feedback.js

[...]

const Update = event => {
  event.preventDefault();
  const id = event.target.querySelector('[name=record_id]').value;

  request({
    url: `${event.target.getAttribute('action')}/${id}`,
    form: event.target
  })
};

const updateForm = document.querySelector('[data-form=update]');
updateForm.addEventListener('submit', Update);

Step 5: Implement the 'Delete' operation

To implement a 'Delete' operation, start by creating a and an . After all, it is roughly analogous to an 'Update' operation

Note

Please refer to the documentation on Data Backup and Removal to review data retention and backups on Insites.

app/views/partials/delete.liquid

{% form html-data-form: 'delete' %}
  <input type='hidden' name='_method' value='DELETE' />
  <label for='record_id'>Record ID</label>
  <input type='text' name='record_id' value='' required>
  <button>Delete</button>
{% endform %}

app/assets/Feedback.js

const Delete = event => {
  event.preventDefault();
  const id = event.target.querySelector('[name=record_id]').value;

  request({
    url: `${event.target.getAttribute('action')}/${id}`,
    form: event.target
  })
};

const deleteForm = document.querySelector('[data-form=delete]');
deleteForm.addEventListener('submit', Delete);

Next Steps

Congratulations! You now know how to implement CRUD operations for database items. Next, visit the documentation on persisting data with database items for further learning: Persisting Data with Database Items