Skip to main content

DHTMLX Kanban. Backend example

A client-side Kanban loses all changes on page reload. Connecting to a backend persists cards, columns, and rows in a database, so the board state survives across sessions and is accessible by any client.

Live example

const { Kanban, Toolbar, RestDataProvider, defaultEditorShape, defaultCardShape } = kanban;

// Check Node.js backend repository here - https://github.com/web-widgets/kanban-node
// Check Go backend repository here -  https://github.com/web-widgets/kanban-go
const url = "https://docs.dhtmlx.com/kanban-backend";
const restProvider = new RestDataProvider(url);

const cardShape = {
    ...defaultCardShape,
    label: true,
    description: true,
    progress: true,
    start_date: true,
    end_date: true,
    priority: true,
    color: true,
    cover: true,
    attached: true,
  	users: {
      show: true,
      values: users
    }
};

const editorShape = [
    ...defaultEditorShape,
    {
        key: "attached",
        type: "files",
        label: "Files",
        uploadURL: url + "/uploads",
    },
];

const send = (partUrl, method, data) => {
    const headers = {
      "Content-Type": "application/json",
    };
    const req = {
      method,
      headers,
    };
    if (data) {
      req.body = typeof data === "object" ? JSON.stringify(data) : data;
    }
  	return fetch(`${url}/${partUrl}`, req).then(res => res.json());
}

Promise.all([
    restProvider.getCards(),
    restProvider.getColumns(),
    restProvider.getRows(),
]).then(([cards, columns, rows]) => {
    const board = new Kanban("#root", {
        cards,
        columns,
        rows,
        rowKey: "row",
        cardShape,
        editorShape,
    });
    board.api.setNext(restProvider);
    new Toolbar("#toolbar", { api: board.api });
  
    board.api.on('move-column', (ev) => {
      send(`columns/${ev.id}/move`, "PUT", ev);
    });
    board.api.on('move-row', (ev) => {
      send(`rows/${ev.id}/move`, "PUT", ev);
    });
});
<!-- component containers -->
<div id="toolbar"></div>
<div id="root" style="height: calc(100% - 56px);"></div>

<!-- dataset -->

<script>
const users = [
        {
            id: 1,
            label: "Steve Smith",
            avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-1.jpg",
        },
        {
            id: 2,
            label: "Aaron Long",
            avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-2.jpg",
        },
        {
            id: 3,
            label: "Angela Allen",
            avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-3.jpg",
        },
        {
            id: 4,
            label: "Angela Long",
            avatar: "https://snippet.dhtmlx.com/codebase/data/kanban/01/img/user-4.jpg",
        },
        {
            id: 5,
            label: "John Doe",
        },
    ];
</script>

The code uses RestDataProvider to load initial data via getCards, getColumns, and getRows, then connects the board to the backend with board.api.setNext(restProvider). This single call routes all board mutations (add, update, delete, move for cards, columns, and rows) through the REST provider automatically. The editor includes a type: "files" field with uploadURL for file attachment support.

Solution overview

  1. Create a RestDataProvider with your backend URL
  2. Load initial data with Promise.all([getCards(), getColumns(), getRows()])
  3. Initialize the board with the loaded data
  4. Connect to the backend: board.api.setNext(restProvider)

Key points

  • setNext handles all operations: After calling board.api.setNext(restProvider), all CRUD and move operations for cards, columns, and rows are automatically sent to the backend - no manual event handling needed
  • File uploads: Add { type: "files", uploadURL: url + "/uploads" } to editorShape for file attachment support
  • Backend implementations available: Reference backends are available in Node.js and Go on GitHub

API reference

Additional resources