How to Make a Simple CRUD Example using ExpressJS and Neo4j
Overview
Today I will write something different in this article. Most of my articles are based on Go and Java. Now I will try to write something with Javascript, a must known programming language for most developers. More precise, I will write a simple CRUD service with NodeJS and Neo4j as a persistence data store.
How to install NodeJS, NVM and Neo4j are beyond the scope of this article.
ExpressJS
ExpressJS is a server-side web framework for NodeJS. It builds on the top of NodeJS. One of the important strength of ExpressJS is, it makes routing very easy. Furthermore, it is the most popular web framework in NodeJS world.
Neo4j
Neo4j is a graph database management system developed by Neo4j, Inc. Described by its developers as an ACID-compliant transactional database with native graph storage and processingยน. It supports Cypher Query Language (CQL) to manipulate data in graph database. For this article, I will use docker to run neo4j instance.
1 |
docker run -p 7474:7474 -p 7687:7687 volume=$HOME/neo4j/data:/data neo4j |
Use Case
For this use case, I will create a service that can insert, update, list and delete a company data. The data will be stored as a graph inside Neo4j.
Step 1: Create Express App
I will create an express app with name expressjs-neo4j
. First, I need to install express-generator
in order to install a wizard to create project setup.
1 |
npm install express-generator -g |
We can create a directory outline just by typing:
1 |
express --view=ejs expressjs-neo4j |
It will generate files and folders that are necessary for express to run. Then type
1 2 |
cd expressjs-neo4j && npm install npm start |
Open a browser http://localhost:3000
to validate that your express is up and running.
Step 2: Installing Modules
There are some modules that are necessary in order to make our service run based on its functionalities. For example, neo4j-driver
module for database driver. There are many more modules to be installed.
1 2 |
npm install morgan body-parser cors neo4j-driver method-override config.json lodash swagger-node-express swagger-jsdoc swagger-ui-express passport passport-jwt jsonwebtoken --save npm install mocha chai chai-http --save-dev |
Step 3: Setup Configuration
Setup the configuration file to configure database connection parameters and several initial values.
1 2 3 4 5 6 7 8 9 10 11 |
// file: config/config.js { "development": { "username": "neo4j", "password": "password", "neo4j": "bolt://localhost:7687", "base_url": "http://localhost:3000", "api_path": "/api/v0", "jwtKey": "SkyIsFallingDown" } } |
There are several steps needs to be configured inside
app.js
. For instance, to enable authorization by usingPassportJS
, compress payload and expose swagger endpoint. See the source code for more details.
Step 4: Database Connection Utility
For getting database session easier, I will create a utility file for database connectivity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// file: neo4j/dbUtils.js var env = process.env.NODE_ENV || 'development'; var config = require('../config/config')[env]; var neo4j = require('neo4j-driver').v1; var driver = neo4j.driver(config.neo4j, neo4j.auth.basic(config.username, config.password)); exports.getSession = function (context) { if (context.neo4jSession) { return context.neo4jSession; } else { context.neo4jSession = driver.session(); return context.neo4jSession; } }; // and the rest |
Step 5: Model
Now create a model as a persistence layer to graph database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// file: models/company.js var Company = require('../models/neo4j/company') var create = function (session, company) { let query = 'CREATE (c:Company{id: {id}, companyName: {companyName}, createdDate: {createdDate}, updatedDate: {updatedDate}}) RETURN c' var writexResultPromise = session.writeTransaction(function (transaction) { // used transaction will be committed automatically, no need for explicit commit/rollback var result = transaction.run(query, { id: company.id, companyName: company.companyName, createdDate: company.createdDate, updatedDate: company.updatedDate }) return result }) return writexResultPromise.then(_returnBySingleId).catch(_handlePayloadValidation) } // and the rest CRUD operations |
The other one to extract data from query results.
1 2 3 4 5 6 7 8 9 10 11 12 |
// file: models/neo4j/company.js var _ = require('lodash'); var Company = module.exports = function (_node) { _.extend(this, _node.properties); if(this.createdDate) { this.createdDate = new Date(this.createdDate.toString()); } if(this.updatedDate) { this.updatedDate = new Date(this.updatedDate.toString()); } }; |
Note: I extend the date property because neo4j date property is a little bit different. See how to get date in the
neo4j/dbUtils.js
for more details.
Step 6: Routes
Create a function to route service endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// file: routes/companies.js var Company = require('../models/company') var writeResponse = require('../helpers/response').writeResponse var dbUtils = require('../neo4j/dbUtils') var uuidv1 = require('uuid/v1') let logging = function (log, message, info) { if (process.env.NODE_ENV !== 'test') { log.info(info, message) } } /** * @swagger * /api/v0/companies: * post: * tags: * - company * description: '' * summary: Add a new company * produces: * - application/json * parameters: * - in: body * name: body * description: Company object that needs to be added to the system. ID will be replaced by system. * required: true * schema: * $ref: '#/definitions/Company' * security: * - Bearer: [] * - api_key: [] * responses: * 200: * description: Company created * 401: * description: Unauthorized. Need JWT. * 409: * description: Invalid payload */ exports.create = function (req, res, next) { let currentDate = dbUtils.getCurrentDate() req.body.id = uuidv1() req.body.createdDate = currentDate req.body.updatedDate = currentDate logging(req.log, 'Create company', { user: req.user, id: req.body.id, companyName: req.body.companyName }) Company.create(dbUtils.getSession(req), req.body) .then(response => writeResponse(res, response)) .catch(next) } // and the rest CRUD functions |
Note: I embed the
swagger-code-gen
to generate documentation inswagger-ui
.
And add the router to app.js
1 2 3 4 5 6 7 8 |
// file: app.js // api routes api.get('/companies', routes.companies.list) api.post('/companies', routes.companies.create) api.put('/companies', routes.companies.update) api.get('/companies/:companyId', routes.companies.getCompanyById) api.delete('/companies/:companyId', routes.companies.remove) |
Step 7: Execute and Run
Since we activate the authorization, so we need to pass the JWT in the request header.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// execute npm run // test via curl curl -X POST http://localhost:3000/api/v0/companies \ -H 'Authorization: Bearer [your jwt token]' \ -H 'Content-type: application/json' \ -d '{"companyName": "XYZ" }' // reponse: { "createdDate": "2018-08-28T11:07:41.948Z", "id": "e8de19e0-aa77-11e8-b7a2-011b2eec63e3", "updatedDate": "2018-08-28T11:07:41.948Z", "companyName": "XYZ" } |
One more interesting information, go to your browser and type http://localhost:3000/api-docs
. You will get the list of your swagger documentation.
Sample Code
That’s from me now. You can see complete source code, including how to run testing using karma
and chai
, on my github.
Great example! Can you elaborate on creating/using the JWT token? Thanks!
I would like to know more about neo4j and its use… you can write me to my email. Grettings