✔️ 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 run
options
The container expose:
- The graphic interface on port
7474
, you can go it through this link. - The
bolt
on 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 neogma
to install the dependency in your project. - Create a file named
appDatabase.ts
in thesrc
folder. - In this file, use Neogma to export a database
client
connected to your database.
💡 You should use the
dbConfig
variable 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:
> [email protected] 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
helpers
about 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
BooksPropertiesI
which define all your fields and their type according to the previous schema.
💡 You must add a property
id
of typestring
to recognize books that has common values.
- Create an empty interface named
BooksRelationNodesI
to 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 theneogma
client 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
createBook
which 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
getBooks
which takes no parameters and return an array of books stored in the database. - Create a function
getBook
that takes anid
as parameter and return the book which add theid
equal to the parameter or null if the id is not found.
- Create a function
updateBook
which 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
Books
factory.
- Create a function
deleteBook
which takes as parameter anid
and 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
Author
in theBooksRelationNodeI
interface. - Add the relationship
Author
in theBooks
factory.
💡 Take a look at this documentation to understand how add relation in your factory.
Create the BooksRelations.ts
file and develop three important function:
linkBookToAuthor
which takes as parameters:bookId
: Book idauthorId
: Author id This function should create the relationWriteBy
between the book and the author and return the book with the author.
You can use the
relateTo
function to create the relation, however, you'll need to create your own query thanks to theQueryBuilder
class. You will need anqueryRunner
instance, you can create a fileappDbInstance
to store it in.
getAuthorBooks
which takes as parameters anid
and 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.ts
file. Don't store it inAuthorEntity.ts
to avoid circle dependencies problem.
detachBookFromAuthor
which takes as parameters:bookId
: Book idauthorId
: Author id This function should delete the relationWriteBy
between 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.js
and replacetestMatch
field 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's
repositories.