Backend & integrations/Node.js
Node.js
This example runs from source. Open the repository to clone, build, and run it locally.
Open on GitHubBackend & integrations
Node.js
A Node.js and Express server that loads Gantt tasks and links from MySQL and saves every edit back over REST.
- README.md
- server.js
- index.html
- schema.sql
README.md View on GitHub
# dhtmlxGantt with Node.js
[](https://dhtmlx.com/)
Implementing backend for [DHTMLX Gantt](https://dhtmlx.com/docs/products/dhtmlxGantt/) using Node.js and Express.
### Requirements
- Node.js
- MySQL
### Installation
1. `npm install`
2. Create database and import **schema.sql**
3. Update connection settings in **server.js**
### Run
- `node server.js`
### Related resources
[Complete tutorial](https://docs.dhtmlx.com/gantt/integrations/node/howtostart-nodejs/)
[Article in our blog](https://dhtmlx.com/blog/dhtmlxgantt-with-nodejs/)
[Video tutorial](https://www.youtube.com/watch?v=D8YzyzBfyP8&feature=youtu.be&ab_channel=dhtmlx)
[DHTMLX Gantt product page](https://dhtmlx.com/docs/products/dhtmlxGantt/)
[Documentation](https://docs.dhtmlx.com/gantt/)
### Support Us
Star our GitHub repo :star:
Check our [roadmap](https://trello.com/b/fhOySHPj/gantt-roadmap) for future updates :wrench:
Read us on [Medium](https://medium.com/@dhtmlx) :newspaper:
Follow us on [Twitter](https://twitter.com/dhtmlx) :bird:
Like our page on [Facebook](https://www.facebook.com/dhtmlx/) :thumbsup:
server.js View on GitHub
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const Promise = require('bluebird');
require('date-format-lite');
const port = 1337;
const app = express();
const mysql = require('promise-mysql');
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({ extended: true }));
app.listen(port, () => {
console.log('Server is running on port ' + port + '...');
});
async function serverConfig() {
const db = await mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: 'gantt-howto-node',
});
app.get('/data', (req, res) => {
Promise.all([db.query('SELECT * FROM gantt_tasks'), db.query('SELECT * FROM gantt_links')])
.then((results) => {
let tasks = results[0],
links = results[1];
for (let i = 0; i < tasks.length; i++) {
tasks[i].start_date = tasks[i].start_date.format('YYYY-MM-DD hh:mm:ss');
}
res.send({
data: tasks,
collections: { links },
});
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
// add new task
app.post('/data/task', (req, res) => {
// adds new task to database
let task = getTask(req.body);
const { text, start_date, duration, progress, parent } = task;
db.query('SELECT MAX(sortorder) AS maxOrder FROM gantt_tasks')
.then((result) => {
/*!*/ // assign max sort order to new task
let orderIndex = (result[0].maxOrder || 0) + 1;
return db.query(
'INSERT INTO gantt_tasks(text, start_date, duration, progress, parent, sortorder) VALUES (?,?,?,?,?,?)',
[text, start_date, duration, progress, parent, orderIndex]
);
})
.then((result) => {
sendResponse(res, 'inserted', result.insertId);
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
// update task
app.put('/data/task/:id', (req, res) => {
let sid = req.params.id,
target = req.body.target,
task = getTask(req.body);
const { text, start_date, duration, progress, parent } = task;
Promise.all([
db.query('UPDATE gantt_tasks SET text = ?, start_date = ?, duration = ?, progress = ?, parent = ? WHERE id = ?', [
text,
start_date,
duration,
progress,
parent,
sid,
]),
updateOrder(sid, target),
])
.then((result) => {
sendResponse(res, 'updated');
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
function updateOrder(taskId, target) {
let nextTask = false;
let targetOrder;
target = target || '';
if (target.startsWith('next:')) {
target = target.substr('next:'.length);
nextTask = true;
}
return db.query('SELECT * FROM gantt_tasks WHERE id = ?', [target]).then((result) => {
if (!result[0]) return Promise.resolve();
targetOrder = result[0].sortorder;
if (nextTask) targetOrder++;
return db
.query('UPDATE gantt_tasks SET sortorder = sortorder + 1 WHERE sortorder >= ?', [targetOrder])
.then((result) => {
return db.query('UPDATE gantt_tasks SET sortorder = ? WHERE id = ?', [targetOrder, taskId]);
});
});
}
// delete task
app.delete('/data/task/:id', (req, res) => {
let sid = req.params.id;
db.query('DELETE FROM gantt_tasks WHERE id = ?', [sid])
.then((result) => {
sendResponse(res, 'deleted');
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
// add link
app.post('/data/link', (req, res) => {
let link = getLink(req.body);
const { source, target, type } = link;
db.query('INSERT INTO gantt_links(source, target, type) VALUES (?,?,?)', [source, target, type])
.then((result) => {
sendResponse(res, 'inserted', result.insertId);
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
// update link
app.put('/data/link/:id', (req, res) => {
let sid = req.params.id,
link = getLink(req.body);
const { source, target, type } = link;
db.query('UPDATE gantt_links SET source = ?, target = ?, type = ? WHERE id = ?', [source, target, type, sid])
.then((result) => {
sendResponse(res, 'updated');
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
// delete link
app.delete('/data/link/:id', (req, res) => {
let sid = req.params.id;
db.query('DELETE FROM gantt_links WHERE id = ?', [sid])
.then((result) => {
sendResponse(res, 'deleted');
})
.catch((error) => {
sendResponse(res, 'error', null, error);
});
});
function getTask(data) {
return {
text: data.text,
start_date: data.start_date.date('YYYY-MM-DD'),
duration: data.duration,
progress: data.progress || 0,
parent: data.parent,
};
}
function getLink(data) {
return {
source: data.source,
target: data.target,
type: data.type,
};
}
function sendResponse(res, action, tid, error) {
if (action == 'error') console.log(error);
let result = {
action: action,
};
if (tid !== undefined && tid !== null) result.tid = tid;
res.send(result);
}
}
serverConfig();
public/index.html View on GitHub
<!DOCTYPE html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<script src="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.js"></script>
<link href="https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css" rel="stylesheet" />
<style type="text/css">
html,
body {
height: 100%;
padding: 0px;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<div id="gantt_here" style="width: 100%; height: 100%"></div>
<script type="text/javascript">
gantt.config.date_format = '%Y-%m-%d %H:%i:%s';
gantt.config.order_branch = true;
gantt.config.order_branch_free = true;
gantt.config.open_tree_initially = true;
gantt.init('gantt_here');
gantt.load('/data');
const dp = gantt.createDataProcessor({
url: '/data',
mode: 'REST',
});
</script>
</body>
schema.sql View on GitHub
CREATE TABLE IF NOT EXISTS `gantt_links` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`source` int(11) NOT NULL,
`target` int(11) NOT NULL,
`type` varchar(1) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE IF NOT EXISTS `gantt_tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`text` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`start_date` datetime NOT NULL,
`duration` int(11) NOT NULL,
`progress` float NOT NULL DEFAULT 0,
`parent` int(11) NOT NULL,
`sortorder` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;