Creating Online Event Calendar with Node.js and dhtmlxScheduler

| Comments (19)

The combination of Node.js and MongoDB is a useful tool for quick development of dynamic sites. In this tutorial, we will explain how dhtmlxScheduler, an embeddable JavaScript calendar, can be used to create an online event calendar with Node.js backend.

download scheduler

You can download the full archive with the final solution from GitHub.

Creating a Simple Node.js Site

One of the advantages of Node.js is a rich library of ready to use solutions. That’s why we won’t write everything from scratch but will use ready modules: Express, as a web-framework, and MongoSkin, to work with MongoDB.

You can find a complete demo of using dhtmlxScheduler and Node.js on GitHub right now.

To start, let’s create a new directory, for example, ‘scheduler-node’, and install the necessary libraries into it by running the next command:

mkdir scheduler-node
cd scheduler-node
npm install express --save
npm install mongodb@"^2.0.0" --save
npm install mongoskin --save

Then we create the base of our application – app.js file in the ‘scheduler-node’ directory – with the following content:

var express = require('express');
var path = require('path');
var bodyParser = require("body-parser");

//connect to the mongoDB
var db = require('mongoskin').db("mongodb://localhost/testdb", { w: 0});

//create express app, use public folder for static files
var app = express();
app.use(express.static(path.join(__dirname, 'public')));

//is necessary for parsing POST request
app.use(bodyParser.urlencoded({ extended: true }));


Due to the use of the Express framework, the whole app fitted into several lines. It doesn’t execute anything extraordinary and just serves static files from the ‘public’ directory.

Let’s create ‘public’ directory and unpack the folder ‘codebase’ from the dhtmlxScheduler package into it (download dhtmlxScheduler Standard Edition). After that, the whole file structure will look like this:

dhtmlxScheduler with Node.js - File structure

Now, we create index.html file in the ‘public’ directory. It will be the main page where we will place our calendar. Here is the content of this file:

<!doctype html>
    <meta content="text/html; charset=utf-8">
    <script src="codebase/dhtmlxscheduler.js" ></script>
    <link rel="stylesheet" href="codebase/dhtmlxscheduler.css">
    <style type="text/css" media="screen">
        html, body{

<script type="text/javascript" charset="utf-8">
    function init() {
        scheduler.init('scheduler_here',new Date(2018,8,4),"month");

<body onload="init();">
    <div id="scheduler_here" class="dhx_cal_container" style='width:100%; height:100%;'>
        <div class="dhx_cal_navline">
            <div class="dhx_cal_prev_button">&nbsp;</div>
            <div class="dhx_cal_next_button">&nbsp;</div>
            <div class="dhx_cal_today_button"></div>
            <div class="dhx_cal_date"></div>
            <div class="dhx_cal_tab" name="day_tab"></div>
            <div class="dhx_cal_tab" name="week_tab"></div>
            <div class="dhx_cal_tab" name="month_tab"></div>
        <div class="dhx_cal_header">
        <div class="dhx_cal_data">

This is the basic HTML code needed for working with dhtmlxScheduler.

To check how the app works, we may start the Node.js server and open http://localhost:3000 address in a browser. If everything is done correctly, we will see a calendar without events (to start Node.js server, run “nodejs app.js” command).

scheduler nodejs

Loading Events

First, we add two new handlers into app.js:

app.get('/init', function(req, res){
        text:"My test event A",
        start_date: new Date(2018,8,1),
        end_date:   new Date(2018,8,5)
        text:"One more test event",
        start_date: new Date(2018,8,3),
        end_date:   new Date(2018,8,8),
        color: "#DD8616"

    /*... skipping similar code for other test events...*/

    res.send("Test events were added to the database")

app.get('/data', function(req, res){
    db.event.find().toArray(function(err, data){
        //set id property for all records
        for (var i = 0; i < data.length; i++)
            data[i].id = data[i]._id;

        //output response

The first “/init” is necessary to generate test data. It just adds a few records into the database.

The second handler – “/data” – will be used to load data into the calendar. Here we choose all the records from the database and send them to the client side as JSON structure.

NOTE: Before sending the records, we define the id property for each object. It’s needed because dhtmlxScheduler expects that record id is stored in but MongoDB keeps this value in obj._id.

We also need to add some changes in index.html:

        scheduler.init('scheduler_here',new Date(2018,8,4),"month");

        scheduler.templates.xml_date = function(value){ return new Date(value); };
        scheduler.load("/data", "json");

We have added two lines. The first one defines how to parse dates from the incoming data. Most often, while loading data from the server, dates are stored like strings in a special format. In our case, dates are stored as timestamps and we can convert them into date objects through Date constructor. The second line initiates data loading, using the URL described above.

If we restart the server and open in browser firstly http://localhost:3000/init – to generate test data, and then http://localhost:3000, we will see our calendar with the events inside.

Adding, Editing, and Deleting Events

To add the ability to add, edit, and delete events in the calendar, we need to add the code that initializes dataprocessor to the index.html file:

        scheduler.config.xml_date="%Y-%m-%d %H:%i";

        var dp = new dataProcessor("/data");
        dp.setTransactionMode("POST", false);

The first line defines the format the dates will take while going back to the server. We are using default year-month-day order that can be parsed by MongoDB. The following block initializes dataprocessor and switches it to the mode of simple POST sending. From now on, each time when the data in the scheduler changes, dataprocessor will call “/data” url and will pass all the properties of the changed event.

Let’s now go over to the server code and add one more handler to app.js:'/data', function(req, res){
    var data = req.body;

    //get operation type
    var mode = data["!nativeeditor_status"];
    //get id of record
    var sid =;
    var tid = sid;

    //remove properties which we do not want to save in DB
    delete data["!nativeeditor_status"];

    //output confirmation response
    function update_response(err, result){
        if (err)
            mode = "error";
        else if (mode == "inserted")
            tid = data._id;

        res.send({action: mode, sid: sid, tid: tid});


    //run db operation
    if (mode == "updated")
        db.event.updateById( sid, data, update_response);
    else if (mode == "inserted")
        db.event.insert(data, update_response);
    else if (mode == "deleted")
        db.event.removeById( sid, update_response);
        res.send("Not supported operation");

Here we use, as all saving operations use POST queries. The first part of the code defines the type of operation and removes all the technical parameters from an incoming query to get an object that can be saved in the database.

Next, depending on the type of operation, we call the related method of db.event for adding/saving/deleting data. As the operations with database are asynchronous, we pass the specified earlier callback function update_response as the last parameter.

The client side expects to get the confirmation of saving operation or an error message – this is what the update_response function is responsible for. It generates a JSON response in the necessary format and sends it to the client side. (Details of the format can be found in the documentation). In case of adding a new record, this function also includes ID generated by MongoDB for the new record, in response to update of the element id on the client side.

If we restart the server and open http://localhost:3000 in browser again, we’ll get a calendar with events that we can create, edit and delete. What is more, all the changes are automatically saved in the database and will be available after reloading the page.

Again, the final demo is available on GitHub so you can download it and view the details.

Download dhtmlxScheduler to add a powerful event calendar to your app

Final Thoughts

The server solution we’ve got is multipurpose. We can add any number of extra fields into the calendar, and we don’t need to change anything in the server code, as it will save and load new fields automatically. The code of data loading and data saving is quite simple. It won’t be difficult to add validation or to format data before loading, if needed.

By slightly expanding this code, we may get a Node.js event calendar that allows several users to edit the calendar simultaneously (update the data on the client side without reloading pages). However, it is a topic for a separate article.


  1. Gor February 25, 2014 at 6:15 am

    Left a message out on GitHub… In regards to CSRF integration. Any help would be greatly appreciated. Thanks! –

    • Nisha June 30, 2014 at 3:45 pm

      I have problem of connectivity with mongodb with above code please give me some options.

      • Ivan (DHTMLX team) June 30, 2014 at 7:10 pm

        Nisha, please describe your problem and post it to our forum:

        • Jessica December 9, 2014 at 3:26 pm

          when trying to run node I got this error= Error: Most middleware (like bodyParser) is no longer bundled with Express and m
          ust be installed separately. Please see

          • Arnaud Malguy December 12, 2014 at 12:04 pm

            I got the same error and fix it with following :
            – Install body-parser : npm install body-parser
            – in app.js :
            – at the beginning add :
            var bodyParser = require(‘body-parser’);
            – replace line “app.use(express.bodyParser());” by

          • Rahul August 31, 2016 at 6:07 pm

            use this instead: “app.use(bodyParser.urlencoded({ extended: true }))”. It will solve ur problem and dont forget to add this also : var bodyParser = require(‘body-parser’). cheers

  2. JB Christy March 4, 2014 at 9:58 pm

    Thanks for this great tutorial and for the sample code! I’m new to node.js and the node ecosystem and really appreciate tutorials like this.

    You mentioned that it wouldn’t be hard to allow several users to update the calendar simultaneously, but “it is a topic for a separate article.” I’d love to learn how to do that. Do you have plans to write that article?

    Thanks again!

    • Ivan March 5, 2014 at 12:25 pm

      Glad that you liked the tutorial. We have plans to write another one that will describe the usecase when several users update the calendar simultaneously but it’s hard to say when it will be available.

  3. Gary Schellhas March 10, 2014 at 11:02 pm

    Could you explain how to add a custom field? I was able to have a checkbox show up and store in the database, but it is not loading correctly when it pulls from the database. Please help!

  4. Binu George February 3, 2015 at 12:10 pm

    The calendar works fine as an independent app. I need to integrate this to the Admin UI of keystoneJS. Can you please give some guidelines for this?

  5. Magnus V March 11, 2015 at 3:24 am


    Love the calendar, but I can´t get it to work with mongo. When I try creating, editing, saving events I get an alert box saying: Not supported operation.

    Do you know how to fix this?

  6. anonymous June 24, 2015 at 2:02 pm


    I am unable to make this work with jade template.
    Can you provide an example in MEAN stack with jade templates?

  7. shahidsha August 8, 2016 at 2:24 pm

    Instead of mongodb can we use mysql or sequelize. if so then please provide me examples to convert mysql db.

  8. Mary J March 12, 2017 at 5:26 pm

    Can anyone help me ?
    I have done exactly as explained above + ‘npm install mongodb’, but I get this error when open localhost:
    Error: invalid schema, expected mongodb
    at module.exports (C:\Users\Mary\Documents\scheduler-node\node_modules\mongodb\lib\url_parser.js:20:11)
    at connect (C:\Users\Mary\Documents\scheduler-node\node_modules\mongodb\lib\mongo_client.js:401:16)
    at Function.MongoClient.connect (C:\Users\Mary\Documents\scheduler-node\node_modules\mongodb\lib\mongo_client.js:225:3)
    at SkinClass.SkinDb._open (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\db.js:35:25)
    at (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\utils.js:127:14)
    at SkinClass.SkinCollection._open (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\collection.js:49:17)
    at (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\utils.js:127:14)
    at SkinClass.SkinCursor._open (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\cursor.js:28:25)
    at (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\utils.js:127:14)
    at SkinClass.(anonymous function) [as toArray] (C:\Users\Mary\Documents\scheduler-node\node_modules\mongoskin\lib\utils.js:83:14)

    • Max March 13, 2017 at 4:31 pm

      It probably happens because you’ve installed newer version of mongodb than used in example.
      It seems like in this case you could try to change connection string from
      “localhost/testdb” to “mongodb://localhost:27017/testdb”.

  9. dhinesh December 2, 2017 at 4:15 pm

    i used this github every thing working fine..expect db of ‘test’ how can i connect and use this for my needs.

    • Aras Kairys (DHTMLX team) December 4, 2017 at 1:44 pm

      Hi Dhinesh,
      Please create a topic on our forum: or use our support system if you are a licensed user.

  10. ValyB January 9, 2018 at 12:22 am


    I have an error when trying to get the data from mongodb: error: uncaughtException: Path must be a string. Received [ { _id: …

    Any suggestions? Thank you.

    • Aras Kairys (DHTMLX team) January 11, 2018 at 7:36 pm

      Hi Valentin,
      Can you please provide us a demo or a code sample so as we can reproduce the problem?
      Please create a topic on our forum: or use our support system if you are a licensed user.

Leave a Reply