DEV Community

MongoDB Guests for MongoDB

Posted on • Originally published at bryntum.com

Bryntum Calendar With MongoDB

This tutorial was written by Arsalan Kattak.

Bryntum Calendar is a high-performance JavaScript component built with an elegant user interface and user experience in mind. With an extensive API customization and numerous features, it integrates easily with React, Angular, and Vue.

One of the advantages of Bryntum Calendar is that it doesn’t bind you to a specific backend technology or database. You’re free to use any backend system and database and connect it with Bryntum Calendar using API endpoints.

MongoDB is a great NoSQL, document-oriented database platform that stores data in a flexible, JSON-like format rather than in rigid relational tables. It is built for horizontal scaling and handles large datasets and high-throughput applications well.

Bryntum and MongoDB calendar

This guide demonstrates how to integrate Bryntum Calendar with MongoDB using Node.js. You’ll learn to do the following:

  • Set up MongoDB.
  • Use Express.js to create a basic Node.js server to interact with MongoDB.
  • Create a basic Bryntum Calendar component with Vite.
  • Improve the Express endpoints to sync data changes from Bryntum Calendar via CRUD (create, read, update, and delete) operations.
  • Add features to the Bryntum Calendar.

Getting started

Before we can connect to MongoDB, we need to configure the frontend app with the Bryntum Calendar component and the backend server.

Setting up the frontend

To begin, we’ll set up the Bryntum Calendar frontend app and prepare the environment.

Clone the frontend app repository and install its dependencies:

npm install
Enter fullscreen mode Exit fullscreen mode

Start the dev server:

npm run dev
Enter fullscreen mode Exit fullscreen mode

You should see the Bryntum Calendar app with MongoDB text on the localhost.

Since Bryntum components are commercial products hosted in a private npm registry, you need to install the Calendar package. To download the package, follow the guide to accessing the Bryntum npm repository. Then, install the package:

  • If you don’t have a Bryntum license, install the trial version by running the following command.
npm install @bryntum/calendar@npm:@bryntum/calendar-trial
Enter fullscreen mode Exit fullscreen mode
  • If you have a license, install the full package instead.
npm install @bryntum/calendar 
Enter fullscreen mode Exit fullscreen mode

In the frontend app files, replace the contents of src/main.js with the following code to render a basic Bryntum Calendar:

import './style.css';
import { Calendar } from '@bryntum/calendar';

const calendar = new Calendar({

    appendTo : 'app',
    date     : '2026-07-01',

    resources : [
        {
            id         : 1,
            name       : 'Default Calendar',
            eventColor : 'green'
        }
    ],
    events : [
        {
            id         : 1,
            name       : 'Meeting',
            startDate  : '2026-07-01T10:00:00',
            endDate    : '2026-07-01T11:00:00',
            resourceId : 1
        }
    ]
});
Enter fullscreen mode Exit fullscreen mode

Add the following import to src/style.css:

@import '../node_modules/@bryntum/calendar/calendar.stockholm.css';

#app {
    height: 100vh;
}
Enter fullscreen mode Exit fullscreen mode

Run the dev server again, and you’ll see the Bryntum Calendar is now rendered in the browser.

Bryntum Calendar rendered in the browser

Setting up the backend

Now that we’ve got a basic frontend app, let’s set up the backend and configure the API endpoints to sync and serve data.

First, clone the backend repository and install the dependencies:

git clone <https://github.com/bryntum/bryntum-calendar-mongodb-vanilla-backend.git>
cd bryntum-calendar-mongodb-vanilla-backend
npm install
Enter fullscreen mode Exit fullscreen mode

The backend is a Node.js server built with Express.js. The main server.js file defines two API endpoints:

  • /load is a GET endpoint that returns calendar data in JSON format.
  • /sync is a POST endpoint that receives CRUD updates from the frontend.

Start the server:

node server.js
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:3000/api/load in your browser (adjust the port if necessary). You should see a JSON response that Bryntum Calendar will use to load events.

Setting up MongoDB

The two most common ways to set up MongoDB are to use MongoDB Atlas to create a cloud-hosted database or to use MongoDB Community Edition to create a local database.

The MongoDB Atlas cloud database is ideal for production applications and large distributed teams:

  • It’s fully managed by MongoDB.
  • You don’t need to install or maintain MongoDB locally.
  • It’s easy to scale, secure, and accessible from anywhere.

The MongoDB Community Edition database is useful for learning, experimenting, or running quick local demos:

  • You can install MongoDB for free on your local machine or server.
  • It requires manual setup, updates, and maintenance.
  • You have full control over the database configuration and data.

The MongoDB Compass GUI allows you to visualize, manage, and import JSON files directly into MongoDB databases.

Installing MongoDB

For this guide, we’ll use the MongoDB Community Edition to install MongoDB locally and use MongoDB Compass to visualize and manage the database.

Follow the official guide to install MongoDB:

Download and install MongoDB Compass.

Follow the instructions in the documentation to start the MongoDB server:

Verify that the server is running by opening a new terminal and running the following command:

mongosh
Enter fullscreen mode Exit fullscreen mode

If the MongoDB shell starts and shows a prompt (>), your server is up and running.

MongoDB shell starting and showing a prompt (>), indicating the server is up and running

Connecting to MongoDB on localhost

By default, when MongoDB is installed and running locally, it listens on port 27017.

The standard connection URI looks like this:

mongodb://localhost:27017
Enter fullscreen mode Exit fullscreen mode

Using MongoDB Compass to create a database

Open MongoDB Compass.

In the New Connection dialog, enter the URI and click Save & Connect. You should now see your local MongoDB server and be able to create or explore databases visually.

New connection

Now, we need to create a database for the Bryntum Calendar and make collections for the Bryntum Calendar stores.

Creating a database for the Bryntum calendar

The Bryntum Calendar contains multiple stores:

Ideally, you would create a collection for each of the stores. To keep it simple for this guide, just create collections for resources, events, and assignments.

Creating collections for  raw `resources` endraw ,  raw `events` endraw , and  raw `assignments` endraw

With that, your MongoDB database is set up.

Populating the MongoDB database

Now, let’s seed it with some initial data that will form a starting point when we connect the database to the frontend and fetch events in the Bryntum Calendar.

Create three JSON files: assignments.json, events.json, and resources.json.

Add the following data to assignments.json:

[
    { "id": 1, "eventId": 1, "resourceId": "arena-alpha" },

    { "id": 2, "eventId": 2, "resourceId": "team-phoenix" },
    { "id": 3, "eventId": 2, "resourceId": "team-shadow" },
    { "id": 4, "eventId": 2, "resourceId": "arena-alpha" },

    { "id": 5, "eventId": 3, "resourceId": "team-aurora" },
    { "id": 6, "eventId": 3, "resourceId": "team-titans" },
    { "id": 7, "eventId": 3, "resourceId": "arena-beta" },

    { "id": 8, "eventId": 4, "resourceId": "arena-alpha" },

    { "id": 9, "eventId": 5, "resourceId": "arena-alpha" },
    { "id": 10, "eventId": 6, "resourceId": "arena-beta" },

    { "id": 11, "eventId": 7, "resourceId": "arena-alpha" },
    { "id": 12, "eventId": 7, "resourceId": "team-phoenix" },
    { "id": 13, "eventId": 7, "resourceId": "team-aurora" },

    { "id": 14, "eventId": 8, "resourceId": "arena-alpha" }

]
Enter fullscreen mode Exit fullscreen mode

Add the following data to events.json:

[
    {
      "id": 1,
      "name": "Opening Ceremony",
      "startDate": "2026-07-01T10:00:00",
      "endDate": "2026-07-01T12:00:00"
    },
    {
      "id": 2,
      "name": "Group Match: Phoenix vs Shadow",
      "startDate": "2026-07-01T14:00:00",
      "endDate": "2026-07-01T16:00:00"
    },
    {
      "id": 3,
      "name": "Group Match: Aurora vs Titans",
      "startDate": "2026-07-01T17:00:00",
      "endDate": "2026-07-01T19:00:00"
    },
    {
      "id": 4,
      "name": "Press Conference - Day 1",
      "startDate": "2026-07-01T20:00:00",
      "endDate": "2026-07-01T21:00:00"
    },
    {
      "id": 5,
      "name": "Semi-Final 1",
      "startDate": "2026-07-02T14:00:00",
      "endDate": "2026-07-02T16:00:00"
    },
    {
      "id": 6,
      "name": "Semi-Final 2",
      "startDate": "2026-07-02T17:00:00",
      "endDate": "2026-07-02T19:00:00"
    },
    {
      "id": 7,
      "name": "Grand Final",
      "startDate": "2026-07-03T18:00:00",
      "endDate": "2026-07-03T20:00:00"
    },
    {
      "id": 8,
      "name": "Closing Ceremony",
      "startDate": "2026-07-03T21:00:00",
      "endDate": "2026-07-03T22:00:00"
    }
  ]
Enter fullscreen mode Exit fullscreen mode

And add the following data to resources.json:

[
    { "id": "team-phoenix", "name": "Team Phoenix", "eventColor": "red" },
    { "id": "team-shadow", "name": "Team Shadow", "eventColor": "blue" },
    { "id": "team-aurora", "name": "Team Aurora", "eventColor": "green" },
    { "id": "team-titans", "name": "Team Titans", "eventColor": "orange" },
    { "id": "arena-alpha", "name": "Arena Alpha (Main Stage)", "eventColor": "purple" },
    { "id": "arena-beta", "name": "Arena Beta (Side Stage)", "eventColor": "teal" }
  ]
Enter fullscreen mode Exit fullscreen mode

In the Bryntum Calendar database, import assignment.json into the assignments collection, events.json into the events collection, and resources.json into the resources collection.

Importing into collections

Integrating MongoDB with the backend

Now that the database is ready, let’s connect it to the backend Node.js server, so that we can populate the Bryntum Calendar using the data and sync changes made in the frontend app to the database.

First, install the MongoDB npm package:

npm install mongodb
Enter fullscreen mode Exit fullscreen mode

Create a .env file with your connection details:

MONGODB_URI=mongodb://localhost:27017
DB_NAME=bryntum_calendar
PORT=3000
Enter fullscreen mode Exit fullscreen mode

Create a new connection.js file for a reusable connection function:

// connection.js
const { MongoClient } = require('mongodb');

const uri = process.env.MONGODB_URI;
const dbName = process.env.DB_NAME;

let db, eventsCollection, resourcesCollection, assignmentsCollection;

async function connectDB() {
  if (db) return { db, eventsCollection, resourcesCollection, assignmentsCollection };

  const client = new MongoClient(uri);
  await client.connect();

  db = client.db(dbName);
  eventsCollection = db.collection('events');
  resourcesCollection = db.collection('resources');
  assignmentsCollection = db.collection('assignments');

  console.log('Connected to MongoDB');
  return { db, eventsCollection, resourcesCollection, assignmentsCollection };
}

module.exports = connectDB;
Enter fullscreen mode Exit fullscreen mode

Then, import it to server.js:

const connectDB = require('./connection');
Enter fullscreen mode Exit fullscreen mode

The /api/load endpoint fetches events, resources, and assignments from MongoDB, converts _id into a string id, and returns the data in the Bryntum format. Replace the /api/load in the server.js:

app.get('/api/load', async (req, res) => {
  try {
    const { eventsCollection, resourcesCollection, assignmentsCollection } = await connectDB();

    const events = await eventsCollection.find({}).toArray();
    const resources = await resourcesCollection.find({}).toArray();
    const assignments = await assignmentsCollection.find({}).toArray();

    const mappedEvents = events.map(({ _id, ...rest }) => ({
      id: _id.toString(),
      ...rest
    }));
    const mappedResources = resources.map(({ _id, ...rest }) => ({
      id: _id.toString(),
      ...rest
    }));
    const mappedAssignments = assignments.map(({ _id, ...rest }) => ({
      id: _id.toString(),
      ...rest
    }));

    res.json({
      success: true,
      events: { rows: mappedEvents },
      resources: { rows: mappedResources },
      assignments: { rows: mappedAssignments }
    });
  } catch (err) {
    console.error(err);
    res.status(500).json({ success: false, message: 'Error loading data' });
  }
});
Enter fullscreen mode Exit fullscreen mode

Similarly, the /api/sync endpoint handles CRUD operation requests. It inserts new records, maps phantom IDs to real MongoDB IDs, updates existing ones, and removes deleted ones. Replace /api/sync in server.js with the following:

const { ObjectId } = require('mongodb');
app.post('/api/sync', async (req, res) => {
  try {
    const { eventsCollection, assignmentsCollection } = await connectDB();
    const { events, assignments, requestId, revision } = req.body;

    const responsePayload = {
      success: true,
      requestId: requestId || null,
      revision: (revision || 0) + 1
    };

    const phantomEventMap = {};

    async function processStore(storeData, collection) {
      const storeResponse = {};

      if (storeData.added) {
        const insertData = storeData.added.map(({ $PhantomId, eventId, ...rest }) => {
          // Special handling for assignments -> map eventId if it's a phantom
          const mappedEventId = phantomEventMap[eventId] || eventId;
          return eventId ? { ...rest, eventId: mappedEventId } : rest;
        });

        const result = await collection.insertMany(insertData);

        const mappings = Object.keys(result.insertedIds).map((key, idx) => {
          const phantomId = storeData.added[idx].$PhantomId;
          const realId = result.insertedIds[key].toString();

          if (collection === eventsCollection) {
            phantomEventMap[phantomId] = realId;
          }

          return { $PhantomId: phantomId, id: realId };
        });

        storeResponse.rows = mappings;
      }

      if (storeData.removed) {
        const objectIds = storeData.removed.map(id => new ObjectId(id));
        await collection.deleteMany({ _id: { $in: objectIds } });

        storeResponse.removed = storeData.removed;
      }

      if (storeData.updated) {
        const updatedRows = [];

        for (const record of storeData.updated) {
          const { id, $PhantomId, ...updateData } = record;
          if (!id) continue;

          await collection.updateOne(
            { _id: new ObjectId(id) },
            { $set: updateData }
          );

          updatedRows.push({ $PhantomId });
        }

        storeResponse.rows = [...(storeResponse.rows || []), ...updatedRows];
      }

      return Object.keys(storeResponse).length ? storeResponse : null;
    }

    if (events) {
      const result = await processStore(events, eventsCollection);
      if (result) responsePayload.events = result;
    }

    if (assignments) {
      const result = await processStore(assignments, assignmentsCollection);
      if (result) responsePayload.assignments = result;
    }

    res.json(responsePayload);

  } catch (err) {
    console.error(err);
    res.status(500).json({ success: false, message: 'Error syncing data' });
  }
});
Enter fullscreen mode Exit fullscreen mode

Update package.json to load .env automatically:

"start": "nodemon --env-file=.env server.js"
Enter fullscreen mode Exit fullscreen mode

Run the server and open http://localhost:3000/api/load. You’ll see the JSON response from MongoDB.

Syncing the backend with the frontend

So far, the frontend uses hard-coded data. Let’s hook it up to the backend so it loads data from the /api/load endpoint and syncs changes through the /api/sync endpoint.

In the frontend’s src/main.js file, remove the events and resources objects and replace them with the following:

crudManager : {
        autoLoad : true,
        loadUrl  : '/api/load'
    }
Enter fullscreen mode Exit fullscreen mode

Create a vite.config.js file with the following code to fix any CORS issues:

// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: '<http://localhost:3000/>',
        changeOrigin: true
      }
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

This proxy redirects frontend requests (like /api/load) to the Node.js backend and resolves CORS issues without requiring changes to the backend code.

After this step, restart the app. The Bryntum Calendar should display the data from your MongoDB database.

Bryntum calendar in the MongoDB database

Next, configure crudManager to send changes back to the server. In the src/main.js, add the following after loadUrl : '/api/load':

       autoSync : true,
        syncUrl  : '/api/sync',
Enter fullscreen mode Exit fullscreen mode

Now, when you create, edit, or delete events, they are persisted in MongoDB. Try adding an event, then refresh the page and confirm that it’s still there.

Adding features to the Bryntum Calendar

The base Bryntum Calendar is functional but plain. Let’s enhance it by enabling a few useful features in the main.js file.

Highlight the selected week in the sidebar:

sidebar : {
    items : {
        datePicker : {
            highlightSelectedWeek : true
        },
        eventFilter : false   // remove the default event filter
    }
}

Enter fullscreen mode Exit fullscreen mode

Add toolbar filters and highlights:

   tbar : {
        items : {
            // Description between the prev and next buttons
            viewDescription : {
                weight : 350
            },
            filterByName : {
                type                 : 'textfield',
                weight               : 650,
                icon                 : 'fa fa-filter',
                placeholder          : 'Find tasks by name',
                clearable            : true,
                keyStrokeChangeDelay : 100,
                triggers             : {
                    filter : {
                        align : 'start',
                        cls   : 'fa fa-filter'
                    }
                },

                // "up." means look in ownership tree. Will be found on the Calendar
                onChange : 'up.onNameFilterChange'
            },
            highlight : {
                type                 : 'textfield',
                weight               : 660,
                placeholder          : 'Highlight tasks',
                clearable            : true,
                keyStrokeChangeDelay : 100,
                triggers             : {
                    filter : {
                        align : 'start',
                        cls   : 'fa fa-search'
                    }
                },

                // "up." means look in ownership tree. Will be found on the Calendar
                onChange : 'up.onNameSearchChange'
            }
        }
    },
Enter fullscreen mode Exit fullscreen mode

Then, define the functions:

 onNameFilterChange({ value }) {
        // We filter using a RegExp, so quote significant characters
        value = value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');

        // A filter with an id replaces any previous filter with that id.
        // Leave any other filters which may be in use in place.
        calendar.eventStore.filter({
            id       : 'eventNameFilter',
            filterBy : event => event.name.match(new RegExp(value, 'i'))
        });
    },

    onNameSearchChange({ value }) {
        value = value.toLowerCase();

        // Loop through all events in the store
        calendar.eventStore.forEach(task => {

            // The cls field is a DomClassList with add and remove methods
            if (value !== '' && task.name.toLowerCase().includes(value)) {
                task.cls.add('b-match');
            }
            else {
                task.cls.remove('b-match');
            }
        });

        // Schedule a refresh of the UI now that we have updated all event UI classes.
        calendar.refresh();

        calendar.element.classList[value.length > 0 ? 'add' : 'remove']('b-highlighting');
    }
Enter fullscreen mode Exit fullscreen mode

Finally, add the following styling to style.css:

.b-highlighting {
    .b-cal-event-wrap {
        opacity : 0.2;

        &.b-match {
            opacity : 1;
            z-index : 10;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With that, your Bryntum Calendar now offers filtering and highlighting.

Bryntum Calendar with filtering and highlighting

Next steps

The Bryntum Calendar component comes with a wide range of features, such as recurring events, different view modes (by day, week, month, or year), and advanced filtering. You can also integrate the Calendar component with other Bryntum tools. Check out the official Bryntum Calendar examples and API documentation to see how you can extend the app you built here.

This tutorial used MongoDB in its simplest form, but the database offers powerful features such as aggregation pipelines, indexing, and schema validation. Dive into the official MongoDB Docs to learn how you can use MongoDB’s advanced capabilities to improve performance and reliability as your database grows.

You may also want to integrate MongoDB with other Bryntum products, like the Grid, Scheduler, Gantt, and Task Board components. Sign up for a free 45-day trial to test component features and experiment with different use cases.

Top comments (0)