✔️ Learn graph database concept
✔️ Interact with a graph database
✔️ Use an OGM to simplify your development
✔️ Understand Cypher syntax
✔️ Build basics query through a query builder
✔️ Test your Typescript code with Jest framework
All the required information to start this workshop can be found in SETUP.md.
A group of publishing houses wants to build a search engine so that their customers can easily find the nearest publishing house selling the book they are looking for.
You have been assigned to the development of the back-end and more specifically, the "data storage and management" part. Your mission is to develop functions to interact with the data stored in a database.
The stored data follow the following scheme:
To store data, several solutions are available to you. First, the use of a relational database, classic but efficient,
this system has proven itself for several years and has been adopted by many companies.
But there are also graph databases, also very old but not very popular, these have remained rather discreet, the community is smaller, the tools less popular but just as effective.
Not knowing what to choose, you ask your supervisor for advice, who asks you the following question:
do your data have close relationships between them, are you going to use these relationships in a recurring way ?
The answer is of course yes ! A book must always be linked to its author and its sales points, otherwise the platform would be useless.
So you decide to store your data in a graph database. It's time to move on to development, let's go for the discovery of graph databases with Neo4j and its Typescript OGM : Neogma.
Our objective is to store data... Wait, where is our database?
Hmm yes, we must run a database to work with! Let's launch neo4j database thanks docker.
docker run -d --name workshop-poc-neo4j -p 7474:7474 -p 7687:7687 -e NEO4J_AUTH=$NEO4J_USER/$NEO4J_PASSWORD neo4j
⚠️ You must load your environment to make this commands work !💡 If you don't understand the command, check
docker runoptions
The container expose:
- The graphic interface on port
7474, you can go it through this link. - The
bolton port7687, you can use it to send request from your code.
Now, we must create a connection directly in the code to communicate with our database.
We will use an OGM to simplify our interaction, let's use Neogma a complete typed OGM for Neo4j.
- Tip
npm install neogmato install the dependency in your project. - Create a file named
appDatabase.tsin thesrcfolder. - In this file, use Neogma to export a database
clientconnected to your database.
💡 You should use the
dbConfigvariable to authenticate your client to your database.
Take a look at the documentation to learn how set up a client.
To verify that your connection is working, modify the file index.ts to print models defined in the database.
It should print an empty object like this:
> typescript-project-template@1.0.0 dev
> ts-node src/index.ts
{}Good job, the database is ready to store some data !
💡 You should take a look at this link to see some
helpersabout models.
Let's store the main entity of our schema: books.
First, create a directory entities in the directory src to store all our entities management functions.
Now, inside this directory, let's create another directory named Books.
Create a file named BooksEntity.ts and define your entity properties in.
To do that:
- Create a type
BooksPropertiesIwhich define all your fields and their type according to the previous schema.
💡 You must add a property
idof typestringto recognize books that has common values.
- Create an empty interface named
BooksRelationNodesIto store all your relation, we will fill it later. - Create a type
BooksInstance, which will be equal to the generic typeNeogmaInstance, filled with the types we previously defined.
Your types are defined, let's create the node:
Export a const variable named Books. It is equal to the result of the function ModelFactory, which create a new model using the types we previously defined, and some additional information we give in the parameters.
The ModelFactory function takes an object which must contain the following fields:
label: the name we choose for the Model, here it'sBook.primaryKeyField: primary key use to recognize entity, here it'sid.schema: defines the data of the model, and applies some constraints to enforce some data safety.
⚠️ Don't forget to pass theneogmaclient as the second argument !
💡 You should take a look at this link to get some examples of entity definition.
Let's develop some functions to interact with our entity. It's common to add elementary CRUD functions when you create an entity.
💡 Be careful, those operations are asynchronous ! Don't forget to use async and await keywords.
You can check this documentation for more information.
You must write those function in a file named BooksModels in the entities/Books folder.
- Create a function
createBookwhich takes book properties as parameters expect the id and create a new book in the database.
💡 You should generate a unique id with the uuid npm package.
The create function return a
BooksInstance, you should create a utility function to convert it toBooksPropertiesI.
- Create a function
getBookswhich takes no parameters and return an array of books stored in the database. - Create a function
getBookthat takes anidas parameter and return the book which add theidequal to the parameter or null if the id is not found.
- Create a function
updateBookwhich takes as parameters:id: book idbookProperties: all properties to update, this parameter must be of typePartial<BooksPropertiesI>to make the all properties optional. Like this, we can update on or more values without problem.
This function must return the updated book.
💡 Take your time to understand what is the return type of the update function provide by the
Booksfactory.
- Create a function
deleteBookwhich takes as parameter anidand return nothing.
⚠️ A deleted node must also close all his relation.
You can add the books.tests.ts file from the tests folder of that repository to test your work.
We can store books, it's cool ! But authors should be awarded for their work no ?
Let's add a new entity named Authors in the entities folder.
Repeat the same process from step 1 to interact with this new entity.
💡 You can do it very fast because the only things that changes are properties of the entity, the management functions should be the same
You can add the authors.tests.ts file from the tests folder of that repository to test your work.
Now, we got books and authors... Let's link them with a simple relation.
In graph database, relations can be represented like human relation. For example: authors wrote those books.
Modify the BooksEntity.ts file to add the relation WriteBy in your book.
- Add the relationship
Authorin theBooksRelationNodeIinterface. - Add the relationship
Authorin theBooksfactory.
💡 Take a look at this documentation to understand how add relation in your factory.
Create the BooksRelations.ts file and develop three important function:
linkBookToAuthorwhich takes as parameters:bookId: Book idauthorId: Author id This function should create the relationWriteBybetween the book and the author and return the book with the author.
You can use the
relateTofunction to create the relation, however, you'll need to create your own query thanks to theQueryBuilderclass. You will need anqueryRunnerinstance, you can create a fileappDbInstanceto store it in.
getAuthorBookswhich takes as parameters anidand return the author with all books that he wrote.
You will need to create a utility function that convert the query result into a readable object.
💡 You can create type in the
BookEntity.tsfile. Don't store it inAuthorEntity.tsto avoid circle dependencies problem.
detachBookFromAuthorwhich takes as parameters:bookId: Book idauthorId: Author id This function should delete the relationWriteBybetween the book and the author and return true if the operation works, false if it failed.
You can add the books-authors.tests.ts file from the tests folder of that repository to test your work.
💡 You can also use the tests to use the complete test suite of the workshop.
⚠️ You will the to modify thejest.config.jsand replacetestMatchfield withtestMatch: ['**/tests/index.ts'],
You learnt how to use neogma and Neo4j! Congratulation 🔥
Now, you can complete your work by implementing others entities.
Remember that schema:
![]() Tom Chauveau |
|---|
🚀 Don't hesitate to follow us on our different networks, and put a star 🌟 on
PoC'srepositories.

