Arsalan Khattak
15 March 2024

Manage data using Bryntum Grid and Directus

Header image of a table using Bryntum Grid and Directus
Bryntum Grid is a high-performance JavaScript table component that’s feature-rich, easily customizable, and integrates with all major frontend JavaScript frameworks. In […]

Bryntum Grid is a high-performance JavaScript table component that’s feature-rich, easily customizable, and integrates with all major frontend JavaScript frameworks. In this tutorial, we’ll show you how to use Bryntum Grid with Directus.

Directus is an open-source data platform you can link to your SQL database to create REST and GraphQL endpoints that you can customize with Node.js. Use Directus to perform CRUD operations on your data, add users, assign roles, create webhooks, and more with ease, and the Directus app dashboard is suitable for non-technical users to view and manage data. The Directus platform is highly customizable, works with new or existing SQL databases, and does not alter your database schema. You can self-host Directus or use the managed cloud service, and there is no vendor lock-in: You can export or migrate your database from Directus.

While you can use Directus as a headless CMS, internal tool builder, or data management and analytics service, this tutorial uses it as a backend service to connect Bryntum Grid to a SQLite database to demonstrate how Directus simplifies creating and modifying API endpoints and connecting them to your database.

We’ll show you how to do the following:

Here is what we’ll build:

Getting started

We’ll use an existing vanilla JavaScript Bryntum Grid app as a starting point. This Bryntum Grid is identical to the Grid we created in our article comparing JavaScript table libraries using Svelte: Bryntum Grid vs. AG Grid vs. DataTables.

Start by cloning the Bryntum Grid starter GitHub repository, which uses Vite as a development server and JavaScript bundler. The completed-grid branch of the repo contains the code for the completed tutorial.

Install the Vite dev dependency by running the following command:

npm install

Now install the Bryntum Grid component using npm. First, get access to the Bryntum private npm registry by following the guide in our docs. When you’ve logged in to the registry, install the Bryntum Grid component by following this guide.

Now run the local development server using the following command:

npm run dev

You’ll see a Bryntum Grid:

This Grid displays example sports player data. A Bryntum Grid AjaxStore is used to get example data from the public/data/data.json file. We’ll modify the AjaxStore configuration once we have created and populated our database with example data, connected it to Directus, and set up Directus API endpoints.

Self-host Directus using Docker

Self-hosting Directus gives you full control over your data and deployment. You can use Docker to self-host Directus. Each new release of Directus is published to Docker Hub. Using Docker has various benefits, including simplifying deployment as it allows Directus to run reliably and consistently across different environments. Refer to the Directus Self-Hosting Quickstart for a detailed guide on running the Directus app with Docker.

Modify the Directus Docker Compose file

In the docker-compose.yml file that you would have created whilst following the Directus Self-Hosting Quickstart, replace the environment variable WEBSOCKETS_ENABLED: "true" with the following:

CORS_ENABLED: "true"
CORS_ORIGIN: "http://localhost:5173"

We set CORS_ENABLED to "true" and CORS_ORIGIN to the URL of the frontend to allow the Bryntum Grid to make API requests to Directus.

To create the application containers and run the Directus app, run the following command in your terminal:

docker compose up

To view the Directus app, open http://localhost:8055 or http://127.0.0.1:8055. Sign in using the admin user credentials in your .env file. In the database folder, you’ll see that a data.db file is created for your SQLite database. Now let’s take a look at the Directus app.

Use the Directus app

The Directus Data Studio app is accessible to non-technical users thanks to its intuitive UI and friendly names for database terms and technical concepts. In Directus, a database is called a Project, a table is a Collection, a column is a Field, a record is an Item, and a datatype is a Type.

Take a look at the Directus Quickstart Guide to see how you can create a Project, Collection, Field, and Item in the app.

We’ll create a player information table in the SQLite database and populate it with example data using the sqlite3 command-line program.

Create a SQLite database table and populate it with example data

To make it easy to work with SQLite database files, there is a set of command-line tools that you can get from the SQLite download page. On macOS, the sqlite3 command-line tools should already be available, but on other operating systems like Windows, you will need to first download this bundle of command-line tools and add the SQLite directory (the path with the sqlite3 executable file) to your computer’s PATH system variable to install the sqlite3 command-line program. Now you can manually enter and execute SQL statements.

Navigate to the database directory in your directus folder:

cd database

Start the sqlite3 program for your database by running the following command:

sqlite3 data.db

Now create a database table for player information data. Paste the following SQL statement into the command prompt and press Enter to execute it:

CREATE TABLE player_info
(
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    name        TEXT DEFAULT NULL,
    city        TEXT DEFAULT NULL,
    team        TEXT DEFAULT NULL,
    percentWins REAL DEFAULT NULL,
    score       INTEGER DEFAULT NULL
);

Insert some example data:

INSERT INTO player_info (name, city, team, score, percentWins) VALUES 
('Dan Jones', 'Los Angeles', 'Stockholm Eagles', 430, 30),
('Harry Smith', 'Dubai', 'Paris Lions', 120, 50),
('Peter Holmes', 'Stockholm', 'Washington Ducks', 90, 55),
('Jacob Reese', 'Moscow', 'Washington Ducks', 500, 43),
('Gilles Girard', 'Paris', 'Moscow Bears', 800, 88),
('Dan Jacobsen', 'Moscow', 'Paris Lions', 920, 93),
('Walter Cruise', 'Barcelona', 'Stockholm Eagles', 100, 44),
('Bill Nielsen', 'Madrid', 'Stockholm Eagles', 750, 55),
('Gareth Freeman', 'Chicago', 'Moscow Bears', 310, 33),
('Ben Newman', 'Los Angeles', 'Washington Ducks', 670, 66),
('Garry Miller', 'Paris', 'Paris Lions', 820, 80),
('Jim Knowles', 'Dubai', 'Stockholm Eagles', 440, 53),
('Victor Reddy', 'London', 'Moscow Bears', 960, 93),
('Bradley May', 'Los Angeles', 'Stockholm Eagles', 200, 40),
('Wesley Malone', 'Berlin', 'Washington Ducks', 760, 29);

You can view the data using the following SQL query:

SELECT * FROM player_info;

To exit the sqlite3 program, type .quit and press Enter.

Open your Directus app in your browser at http://localhost:8055 or http://127.0.0.1:8055 and navigate to Settings Module > Data Model. You’ll see the Player Info Collection from the SQLite database. Click on it to configure it.

Notice that Directus has figured out the field names and data types from our database column. Directus uses database introspection to do this.

Now navigate to the Content Module by clicking the three-dot menu to the right of the Player Info element and selecting View Content. You’ll see the example data:

If you don’t see the items, try refreshing the page. Note that you can add extra fields to display using the “Add field” plus button to the right of the table headings.

The information panel on the right has options to auto-refresh the data at set intervals or import and export data.

Control API access to the collection

To give Bryntum Grid access to the collection, we’ll make all CRUD operations on the player_info table public by setting the collection permissions. Directus has two built-in roles: Admin and Public. The public role determines what’s accessible to non-authenticated users.

Open the Settings Module > Access Control page and click on the Public role.

The symbols represent create, read, update, delete, and share access for the player_info collection. The red icons represent no access. Click on the icon to change the access. We’ll make all the CRUD operations “All Access” for this collection. Now we’ll be able to perform CRUD operations on this collection from our Bryntum Grid without needing authentication.

Connect Bryntum Grid AjaxStore to Directus API endpoint to read data

Let’s get the data from Directus into our Bryntum Grid. In your Bryntum Grid app, change the readUrl in the AjaxStore store variable in the main.js file to the following:

  readUrl: "http://localhost:8055/items/player_info",

This will read the player info data from the API endpoint created by Directus.

Run the development server using the following command:

npm run dev

You’ll see the data from your SQLite player_info table. The API endpoint created has the following URL structure:

/items/:collection

You use a GET request to read, a POST request to create, a PATCH request to update, and a DELETE request to delete data through the API endpoint. You can read more about these endpoints in the Directus Items API reference docs page.

On the client side, we use an AjaxStore to handle CRUD operations in the Bryntum Grid. The response from the Directus API for reading the player information data matches the response structure expected by the Bryntum Grid AjaxStore. The responses for the other CRUD operations do not. We’ll create custom Directus API endpoints to return the response in the correct format expected by the Bryntum Grid AjaxStore.

Create custom Directus API endpoints to create, update, and delete items

custom Directus API endpoint allows you to register new API endpoints.

Move into the extensions directory:

cd extensions

A custom API endpoint is a Directus Extension that allows you to modify or expand Directus’ functionality.

To create an extension, we’ll use the create-directus-extension utility:

npx create-directus-extension@latest

In the options that follow in the command line, input the following:

In the directus-extension-endpoint-player_info/src/index.js file, replace the code with the following lines of code:

const collectionName = "player_info";
export default {
  id: "player_info-api", // API endpoint name
  handler: (router, context) => {
	
    const { services, getSchema } = context;
    const { ItemsService } = services;
    router.post("/", async (req, res) => {
      const itemsService = new ItemsService(collectionName, {
        schema: await getSchema(),
        accountability: req.accountability,
      });
      try {
        const createdId = await itemsService.createOne(req.body);
        // Retrieve the full item data
        const fullItemData = await itemsService.readOne(createdId);
        const modifiedData = {
          success: true,
          data: [fullItemData],
        };
        res.json(modifiedData);
      } catch (error) {
        console.error("Error:", error);
        res.status(500).json({
		  success: false,
		  message: "There was an error creating an item.",
		});
      }
    });
  },
};

By default, the endpoint root will be the name of the extensions folder, which is /directus-extension-endpoint-player_info/ in this case. To change the endpoint root, we set the id property. Our API endpoint will now be: http://localhost:8055/player_info-api.

The handler is the endpoint’s registration handler function. We use its router parameter to modify specific API requests. The context parameter has various properties, of which we use services and getSchema. The services property contains all API internal services. The getSchema property is an async function that reads the available schemas for use in services.

The ItemsService allows you to perform CRUD operations on items in a collection. We use its createOne method to create a new player information item in our database based on the data sent to the API endpoint in a POST request.

We then modify the response to match the response expected by the Bryntum Grid AjaxStore.

Now add the following route handler methods below the router.post route handler method:

    router.patch("/:itemId", async (req, res) => {
      const itemsService = new ItemsService(collectionName, {
        schema: await getSchema(),
        accountability: req.accountability,
      });
      // Extract itemId from the URL
      const itemId = req.params.itemId;
      try {
        const updatedId = await itemsService.updateOne(itemId, req.body);
        // Retrieve the full item data
        const fullItemData = await itemsService.readOne(updatedId);
        const modifiedData = {
          success: true,
          data: [fullItemData],
        };
        res.json(modifiedData);
      } catch (error) {
        console.error("Error:", error);
        res.status(500).json({
          success: false,
          message: "There was an error updating an item.",
        });
      }
    });
    router.delete("/", async (req, res) => {
      const itemsService = new ItemsService(collectionName, {
        schema: await getSchema(),
        accountability: req.accountability,
      });
      try {
        await itemsService.deleteMany(req.body);
        res.json({ success: true });
      } catch (error) {
        console.error("Error:", error);
        res.status(500).json({
          success: false,
          message: "There was an error deleting items.",
        });
      }
    });

These two route handlers will handle update and delete operations.

The itemsService CRUD methods expect the data passed as arguments to have a specific structure. We’ll modify the data sent by the Bryntum Grid before it’s added to the SQLite database. We can do this in the route handlers. For this tutorial, we’ll do it on the client side to show how you can do it by configuring the Bryntum Grid AjaxStore.

Modify the Bryntum Grid AjaxStore to sync data changes to the Directus API endpoints

In the main.js file in your Bryntum Grid app, add the following properties to the AjaxStore store variable:

    createUrl: "http://localhost:8055/player_info-api",
    updateUrl: "http://localhost:8055/player_info-api",
    deleteUrl: "http://localhost:8055/player_info-api",
    autoCommit: true,
    useRestfulMethods: true,
    httpMethods: {
      read: "GET",
      create: "POST",
      update: "PATCH",
      delete: "DELETE",
    },

We set the create, update, and delete URLs and HTTP methods for each CRUD operation.

To modify the data sent to our custom Directus API endpoint, we’ll use the beforeRequest event. Add the following listeners property to the AjaxStore configuration:


listeners: {
      // change request body as needed
      beforeRequest: (event) => {
        if (event.action === "create") {
          const newItem = event.body.data;
          delete newItem.id;
          event.body = newItem;
        }
        if (event.action === "update") {
          const updatedItem = event.body.data;
          const itemId = updatedItem.id;
          delete updatedItem.id;
          event.body = updatedItem;
          store.updateUrl = http://localhost:8055/player_info-api/${itemId};
        }
        if (event.action === "delete") {
          const itemIds = event.body.ids;
          event.body = itemIds;
        }
      },
    },

When the AjaxStore sends a request, we listen for the beforeRequest event to modify the data sent in the event.body to match the data structure required by the custom Directus API endpoints for create, update, and delete CRUD operations.

Note that we add the required itemId route parameter for the update operation by modifying the store.updateUrl property.

Add the custom Directus API endpoints to Directus

We need to build the custom endpoint to use it with Directus.

First, stop the Directus app from running by exiting the running Docker container. Do this by pressing Ctrl + C in your terminal.

Next, move into the directory with your custom Directus API endpoint:

cd extensions/directus-extension-endpoint-player_info

Now build the endpoint using the following command:

npm run build

Once that’s done, move back to your root directory:

cd ../..

Now run the following command to stop and remove the containers, networks, and other resources that were created using docker compose up:

docker compose down

Run your Directus app again using the following command:

docker compose up

Open http://localhost:5173/ to view your Bryntum Grid, which now has full CRUD functionality.

Next steps

This tutorial covers the basics of using Bryntum Grid with Directus. As a next step, you could add your Bryntum Grid to an app with user roles to take advantage of the Directus Roles and Permissions feature. You could set up your app so that some users can only read the Bryntum Grid data and others can perform specific CRUD operations.

You can also take a look at the Bryntum Grid examples page to see additional features that you can add, such as:

Arsalan Khattak

Bryntum Grid Development