Learn how to use these two technologies together to build a REST API This article will teach you how to use Node.js, Fastify and HarperDB to build a course management system. This application will help you track the courses you are doing and the courses you plan to do. You will use the following technologies: Node.js HarperDB Fastify Introduction When it comes to Node.js and Fastify, people are familiar with these technologies. However, when it comes to databases, there is a new kid on the block! Recently, I took a break from the databases I usually use to play around with HarperDB. is a distributed database with NoSQL and SQL capabilities. The thing about HarperDB is that you perform all the CRUD database operations using one endpoint. HarperDB most fascinating only The most notable features of HarperDB are: using a single endpoint execute SQL queries on JSON data it removes the need for an ORM by returning the results as JSON arrays the ability to insert data via JSON, CSV or using SQL Does not that look interesting? Prerequisites Before going further, there are some prerequisites you should be aware of. The tutorial assumes basic knowledge of: Node.js and FastifyJS Databases JavaScript Knowing how to use a package manager like npm Additionally, you should have a Node version of 12.xx or higher for HarperDB to run. Lastly, you need to have a HarperDB account, which you can create . Do not worry; it's ! here free Set up the folder structure The first step of the tutorial is to set up the folder structure of the application. Create a new folder called and open it. You can do it with the following commands: course-management mkdir course-management cd course-management The basic folder structure is ready. Of course, when you build each part of the application, you'll create more folders. Install dependencies At this point, you should be in the folder. Let's initialise the project with the following command: course-management npm init -y The flag generates the "package.json" automatically without you having to answer the usual questions about the project name, description and so on. If you open "package.json", you can see that all fields are autocompleted. -y Install the required packages The next step is to install the packages needed to build the application. You can install multiple packages in one line. Run the command below in your terminal: npm i fastify harperive dotenv --save The command installs three packages/dependencies: - FastifyJS is a new web framework, and it claims to be the fastest web framework in town. Fastify - It's the Node.js driver for HarperDB. Harperive - A module that loads environment variables from an .env file to process.env. Dotenv Configure the Fastify server Before proceeding to configure the server, you need to create a few files. The first step is to create the following files: touch app.js touch .env The file contains the server configuration. On the other hand, the file stores your environment variables. Open the file and write the following code: app.js .env app.js app = ( )({ : }); ( ).config() app.get( , (req, res) => { res.send({ : }); }); app.listen(process.env.PORT, (err, addr) => { (err) { app.log.error(err); process.exit( ); } app.log.info( ); }); const require 'fastify' logger true require 'dotenv' '/' hello 'world' if 1 `Your server is listening on port 🧨` ${process.env.PORT} With the first line, you import the Fastify package and store it in a constant named . In the same line, you also enable the . After that, you import so you can use environment variables. app logger dotenv Lastly, you create a GET route that responds with JSON, and then you listen on the port specified in the file. .env Moving on, open the file and add the following lines: .env PORT= INSTANCE_URL= INSTANCE_USERNAME= INSTANCE_PASSWORD= INSTANCE_SCHEMA= 3000 The fields starting with will hold information about the database. Now, it's time to create a HarperDB account if you do not have one already. INSTANCE Create and configure the database First of all, login into the . After you log in, click on the button, which creates a new HarperDB Cloud Instance. Figure 1 illustrates that! HarperDB Studio + Figure 1 After that, you will be asked to choose either a "New HarperDB Cloud Instance" or "Register User-Installed Instance", as shown in figure 2. Figure 2 Click on the button saying " ". As a result, you create a database instance in their cloud rather than hosting it yourself. After that, you need to enter: Create HarperDB Cloud Instance the name of the instance a username a password In figure 3 below, you can see my instance information. Figure 3 The next step is to choose your . For this tutorial, I advise you to choose the free options. You get the following specs for free: instance specifications 0.5GB RAM 1GB of storage and 100 IOPS Figure 4, below, illustrates that! However, if you plan to use the DB heavily, I recommend powering up the instance and choose better specifications! Figure 4 The last step is to confirm the instance details and then wait for the instance to spin up. After the instance is set up and initialised, click on the instance. For example, you can see my newly-created instance in figure 5 below. If you are accessing the instance for the first time, you might be prompted to enter the username and password you set previously. Figure 5 Your instance does not have any schemas or tables at the moment. As a result, you need to create a schema first! You can see that I chose as the name for my schema (figure 6). However, you can use any other name for your schema. mydb Once you chose a name for your schema, click on the green checkmark to save it! Figure 6 Lastly, you need to create tables. For the moment, you'll set up only one table called . Enter the name "courses" and provide a name as well. The "hash attribute" is used to uniquely identify each record. In simpler words, the "hash attribute" is the ID, which is unique for each record. Now, click on the green checkmark to save it. As shown in figure 7. courses hash_attribute Figure 7 Now, you are ready to use the database in the Node.js application! Set and config HarperDB in the app Go into the .env file and add the following lines: INSTANCE_URL=https: INSTANCE_USERNAME=admin INSTANCE_PASSWORD=password INSTANCE_SCHEMA=mydb //app-tutorial.harperdbcloud.com/ Of course, you need to change those details with yours! The above information is fictive, so it will not work if you try! Save the file, and let's move on! Now create two folders and a database file. You can do so by following these commands: mkdir src mkdir src/db touch src/db/db_config.js With the above commands, you create the "db" folder inside "src". Also, you create the file "db_config.js" that stores the configuration for the database. Open the newly-created file and write the following code: db_config.js harperive = ( ).Client; DB_CONFIG = { : process.env.INSTANCE_URL, : process.env.INSTANCE_USERNAME, : process.env.INSTANCE_PASSWORD, : process.env.INSTANCE_SCHEMA }; client = harperive(DB_CONFIG); .exports = client; const require 'harperive' const harperHost username password schema const new module With the first line, you import the Node.js driver for HarperDB. After that, you create the database configuration with the details stored in the file. Lastly, you create a new client and export it. .env You'll use the client later, in the controller file! For now, let's move to the routes and controllers. Configure app endpoints The first step is to create two new folders in the folder. Create them as follows: src mkdir src/controllers mkdir src/routes After that, you need to create two new JavaScript files in the newly-created folders. Create the files and as follows: course.js courseController.js touch src/routes/course.js touch src/controllers/courseController.js You'll start by creating the routes. In this scenario, you can make use of the FastifyJS plugin architecture. So open the file from the "routes" folder, and write the following code: course.js courseController = ( ); { } .exports = routes; const require '../controllers/courseController' async ( ) function routes app, opts module There are three things happening: You import the controllers used for routes. You create a routes function. You export the newly-created routes function. In this method - routes - you configure the endpoints of your application. For each route, you specify the , the and the at minimum. HTTP method URL handler However, there are many other parameters. You can see the complete list here . With that being said, let's build the first endpoint that returns all the courses when accessed. You can do so by calling the method on the , as follows: route app courseController = ( ); { app.route({ : , : , : courseController.getCourses }); } .exports = routes; const require '../controllers/courseController' async ( ) function routes app, opts // Get all courses method 'GET' url '/courses' handler module You build all the other routes/endpoints in a similar fashion. The only things that change are the method, URL and handler. In the Gist below, you can see all the endpoints. You have an endpoint for each CRUD operation. Application users can see all the courses, a specific course, add new courses, delete and edit existing courses. courseController = ( ); { app.route({ : , : , : courseController.getCourses }); app.route({ : , : , : courseController.getSpecificCourse }); app.route({ : , : , : courseController.addCourse }); app.route({ : , : , : courseController.editCourse }); app.route({ : , : , : courseController.deleteCourse }); } .exports = routes; const require '../controllers/courseController' async ( ) function routes app, opts // Get all courses method 'GET' url '/courses' handler // Get a specific course method 'GET' url '/courses/:id' handler // Add a course method 'POST' url '/courses' handler // Edit a course method 'PUT' url '/courses/:id' handler // Delete a course method 'DELETE' url '/courses/:id' handler module : Each route/endpoint has a specific controller, as you can see. At the moment, they are just placeholders because there are no controllers. That means, if you try to access the routes they won't work! But that changes in the next step because you'll build the application logic now. Note Go to the in the root folder, and add the following line: One more thing before going further. app.js app.register( ( )); require './src/routes/course' The purpose of that line is to make the routes available in your application. If you do not add this line, your routes will not work. The final version of should look as follows: app.js app = ( )({ : }); ( ).config() app.register( ( )); app.listen(process.env.PORT, (err, addr) => { (err) { app.log.error(err); process.exit( ); } app.log.info( ); }); const require 'fastify' logger true require 'dotenv' require './src/routes/course' if 1 `Your server is listening on port 🧨` ${process.env.PORT} Build application logic with HarperDB The application logic defines what happens when a user makes a request to the routes specified in the previous section. As an example, when someone accesses the route , they should see all the courses from the database. As a result, let's start with that route. /courses The code for all routes will be in . Thus, open the file and import the "Harperive" client at the top of the file: courseController.js courseController.js client = ( ); const require "../db/db_config" Now, this where the fun begins. To portray the capabilities of HarperDB, you'll use both SQL and NoSQL operations to manipulate data. Build the GET route In the same file, add the following code after importing the database client: getCourses = (req, res) => { allCourses = client.query( ); res.send({ allCourses }); }; .exports = { getCourses, } const async const await 'SELECT * FROM mydb.courses' module As you can see, the HarperDB Node.js driver allows you to run SQL queries on your database. The above query selects and returns all the courses from the database. After that, you use the object to send the data to the client. If you run the application and try to access the route, you won't get any data because the database is empty. response /courses Figure 8 Figure 8 illustrates a successful request. You can see that the "data" array is empty because the database is empty. However, we'll repeat the request once there is data in the database. Build the POST route The next step is to build the POST route so you can add data into your database. In this case, we'll use a NoSQL operation. Write the following code after the constant: getCourses addCourse = (req, res) => { allProperties = .keys(req.body).length; (allProperties !== ) { res.code( ).send({ : }); ; } { newCourse = client.insert({ : , : [ { : req.body.name, : req.body.description, : req.body.author, : req.body.link } ] }); res.send({ newCourse }); } (error) { res.send({ error }); } }; .exports = { getCourses, addCourse, } const async const Object if 4 400 error 'Some properties are missing! Add the name, description, author and link!' return try const await table 'courses' records name description author link catch module First of all, the part before the block makes sure the user provides all the fields. If the user tries to add a course without specifying the , , or , it will return an error. The user must always provide all four fields. try catch name description author link Then, in the block, you use the database client to insert the new course into the database. try catch - You need to specify the table (which in this case is ) and then the . courses records - The field is an array of objects, which means you can enter multiple courses in one go. However, for now, one is enough. records Provided the insertion is , it returns the newly-added course. You can see the route in action in figure 9 below. successful Figure 9 The operation was successful, and the new course was added to the database! Figure 10 shows the course in the database! Figure 10 GET a specific course Getting a specific course is similar to getting all courses, but with one addition. Now, you use a clause to select a specific course. WHERE You pass the course "ID" in the URL, and then the SQL query matches that with a record from the database. But first of all, , you might ask! There are two options ( ): how do you get the ID? you need courses in the database first 1. You can visit the route and get the ID of any course you want. http://localhost:3000/courses 2. Go to the HarperDB studio, click on the course, and you'll see all the details. At the top of the page, you'll see that it specifies which one is the ID - see figure 10 above. getSpecificCourse = (req, res) => { course = client.query( ); res.send({ course }); }; .exports = { getCourses, addCourse, getSpecificCourse, } const async const await `SELECT * FROM mydb.courses WHERE id=" "` ${req.params.id} module After that, it returns the course. Figure 11 below illustrates what happens when you make a GET request to get a specific course! Figure 11 DELETE a specific course You can delete a course by providing the course ID in the URL. For example, if you make a request to , you delete the course with the specified ID. DELETE http://localhost:3000/courses/0eb95456-255b-44da-b3df-b90671ef0908 deleteCourse = (req, res) => { course = client.query( ); res.send({ : , : course }); }; .exports = { getCourses, addCourse, getSpecificCourse, deleteCourse } const async const await `DELETE FROM mydb.courses WHERE id=" "` ${req.params.id} message 'Course deleted!' deleteCourse module Once the course is deleted, it sends a message confirming that and some more extra information. Figure 12 illustrates a "DELETE" request. Figure 12 Build the PUT route Lastly, this route allows you to update an existing course. All the code before the block makes sure the users only use the allowed fields - , , and . If the user tries to update a field that does not exist, the server will respond with an error. try catch name description author link To edit the course, you're using a NoSQL operation again. You call the method on the database client, then pass the table name and the records you want to update. update (courses - in this case) Also, it's essential to specify the in the records array. If you do not, the server does not know which record to update. id editCourse = (req, res) => { updates = .keys(req.body); allowedUpdates = [ , , , ]; isValidOperation = updates.every( allowedUpdates.includes(update)); (!isValidOperation) { res.code( ).send({ : }); ; } { updatedCourse = client.update({ : , : [ { : req.params.id, : req.body.name, : req.body.description, : req.body.author, : req.body.link } ] }); res.send({ updatedCourse }); } (error) { res.send({ error }); } }; .exports = { getCourses, addCourse, getSpecificCourse, deleteCourse, editCourse } const async const Object const 'name' 'description' 'author' 'link' const ( ) => update if 400 error 'Not a valid operation! ' return try const await table 'courses' records id name description author link catch module If there is an error, the server sends back the error. Otherwise, it returns the updated course. The figure below shows how to make a PUT requests and what happens it's successful! Figure 13 That's all about routes and controllers! You can see the whole file in . I chose not to embed it because it is big and takes a significant space on the page! courseController.js this Gist Similarly, you can . To learn better, go through it, change stuff, break stuff, add things and so on! see the complete application on my GitHub How to start the application You can start the application by running node in the root folder. app.js However, if you clone the application from GitHub, you need to install the dependencies first. You can do so by running: npm install node app.js Conclusion By this point, you should have a REST API that allows you to store and manipulate courses. The purpose of this tutorial was to get up-to-speed with the two technologies. As a result, other things could be added, such as: Authorization API rate limiting Adding a front-end and transform it into a full-stack application With that being said, if you like the tutorial and want part 2, where I build the front-end with Vue, let me know in the comments! Previously published at https://catalins.tech/build-a-rest-api-with-harperdb-and-fastifyjs