Fullstack CRUD application using Fastify, React, Redux & MongoDB Part-2

Let’s create something interesting

Jatin Kumar
Techspace
15 min readMay 27, 2019

--

Hi there, This tutorial is the final installment of the my two part tutorial series, in which we are building a Fullstack Application using Fastify, React, Redux and MongoDB, which would support all CRUD functionalities. In case you didn’t read the first part, I’ll highly recommend you to do that before you move any forward because this tutorial would be the continuation of Part 1 and in order to understand what’s going on you are expected to have at least some basic understanding of our frontend that we made in Part 1. You don’t need to know meaning of every single line we wrote there. You just need to know about how the data is flowing across the app and what event handlers are making things as they are. A quick skim of the blog will do the job. File structure was discussed early on in the Part 1 blog, So you just need to create clone of that file structure and copy the respective codes in the their corresponding files and you are good to go. You can read Part-1 from here.

So, now that you have skimmed the blog and have the code of React Redux app that we built in Part 1. I expect you to have some basic understanding of the current code base. So, if you played with the incomplete version of the app that we built in part 1, you may have realized that it lacks persistence to changes. Our redux store is wiped out every time we reload the application. So, in this Part of tutorial series we’ll be making our application persistent to changes. As a bonus section I’ll also guide you over deployment process. So without wasting any time, Let’s get started!

Project Setup for Server

If you remember from Part 1, we already have a project setup. We just need to fill up our vacant server directory with some server related files. so let’s get this done.
Navigate to server/ > Open your terminal > execute the commands shown below.

Open .env and paste couple of lines given below

Now you must be having your file structure inside server directory quite similar to as shown below

Now let’s install some node modules that we’ll require for this part of the tutorial

After getting required modules installed we are ready to write some code. We’ll start by building backend api using fastify + MongoDB and then in later half of the tutorial we’ll spending time in connecting our fastify backend api to our react redux frontend.

Backend API

In this section We’ll create Mongoose Schemas, controllers, routes, and then finally our fastify.js server. Let’s kick off with building our mongoose schema.

Step 1: Open server/models/menuItems.js and copy the code shown below.

So that’s how our mongoose schema looks like. Here we are having an id property which stores the id that we create with the help of uuid.v4() in our <Menu/> component. We also have name and price which would store name and price of the item respectively. After creating schema we create a model which is then exported so that other files like controller.js could make use of it. Next up we’ll create controllers.

Step 2: Open server/controller.js and copy the code shown below.

So that’s our controllers. Controllers are basically not so different from event handlers that we use in react. It’s just that their way of getting triggered is different. Each controller defined above would be attached to a route and whenever that particular route will be requested, the responsible controller will get invoked. Here we are avoiding chaining of promises by making use of async and await. In case you never heard these terms before, you can visit here to know more about them. Now let’s discuss all the controllers one by one.

  • fetchItems : As name implies it’s used to fetch all the items present in database. It takes benefit of find() API provided by mongoose. Here we don’t pass any argument to it, that’s why it returns every single item present in database.
  • addItem: It’s used to add a new Item to database. Here we first create a new Item by creating a new instance of MenuItem model. Content for new item is received via req.body. After creating new item, we add it to database using save() API provided by mongoose.
  • updateItem: It’s used to update item. Here we use findOneAndUpdate() API provided by mongoose to find item with the id = req.params.id and update it’s content, which we receive via req.body.
  • deleteItem: It’s used to delete a certain item whose id matches with id that it receives as argument. It takes help of findOneAndUpdate() API provided by mongoose.

Note : In case you are curious that why we are not using findByIdAndUpdate() and findByIdAndDelete() as we are doing of all manipulations in data using id, then you should know that they only work with _id(which mongoose generates automatically every time an item is created). And the id that we are using is not equal to _id . The id that we are using comes from the React frontend and it get’s generated using uuid.v4() whenever we add item to our redux store. We are using it because along with MongoDB Database we are also managing a redux store. And when we save items to database we don’t fetch the created item simultaneously, instead we save them locally as well in redux store and work with that copy. We do this to provide better user experience and It’s a very popular practice among react developers in which we’re updating the client locally before waiting to hear from the server. It’s also commonly referred to as optimistic updating. So basically our react frontend at the time of creating items is unaware of _id property till it fetches the item from database which only happens when we load or reload our application. So if we use _id instead of id then apparently our redux functionality would break. And I don’t think that requesting data from database every time we make changes in your application would be performance efficient under slow connections. So that’s why apparently we are managing two ids _id and id and why we are using findByIdAndUpdate() and findByIdAndDelete(). If all that’s confusing to you forget about it and return here after finishing this tutorial, then you’ll understand what I am trying to say exactly.

So that’s all for our controller.js, Next up we’ll setup routes and make these controllers useful by attaching them to appropriate ones.

Step 3: Open server/routes.js and copy the code shown below.

So that’s what our routes.js looks like, In this notation we are specifying a method , url, and handler for every individual router. Here we are assigning a appropriate controller to every route as a handler. Whenever we request any route, the corresponding controller get’s invoked i.e. when we request GET <domain_name>/api/menuItems , fetchItems get’s invoked immediately and returns result. Next up we’ll be wrapping up with our backend API by creating our fastify server.

Step 4: Open server/server.js and copy the code shown below.

So that’s our fastify server. Now before we go any further, we don’t have any database yet. so let’s get this done quickly. For the time being we’ll be using MongoDB Atlas. In case you are new to MongoDB Atlas, please visit there getting started guide to setup the project (and while doing setup don’t load any sample data, we don’t require it). After successfully setting up the project click on connect button and then select connect your application and copy the connection string. Now open your server.js file and locate mongoose.connect. After finding it replace old connection string with the new one that you just copied in your server.js. Okay so, if you followed the getting started guide correctly you must have added a user. Now, open .env file and replace the <USER_PASSWORD> with your actual user password. Now I think we have the required setup to go ahead.
Now if everything goes right you must be seeing the two log statements shown below in your console when you run node server/server.js.

And if you navigate to http://localhost:3000. You must be seeing {msg : hello, world!} in your browser as output.

Now let’s discuss our server in detail. So first off we connect to our database. And then we’ve placed a dummy route which currently does nothing special than printing {msg : hello, world!} but later on it would play an important role as it will be serving our react application. In next instruction we iterate over all of the imported routes (that we created inroutes.js) and register them with fastify (which in a nutshell means that we’re making fastify aware of routes that we’ve declared elsewhere). After doing it we bind our server to a port using fastify.listen()

So that’s all for our backend API. Now let’s test our API and best tool to do it is Postman. We will be sending our data as raw objects in the body of the request and as parameters. Don’t forget to run server before testing API.

Saving Item to Database

Reading all the items

Updating item

Deleting Item

We now have a fully functional API — but what about the React Frontend? Let’s wire up them together up next! I assure you it would be fun.

Wiring up React with Fastify

Step 1: Serve react app using fastify server

If you see your project directory you’ll notice that there is folder named dist (provided that you have built react frontend at least once) that we didn’t create. It actually contains compiled form of our react app and it get’s created automatically every time we build our react app using parcel. So in order to host our react application we just need setup the dist directory as default lookup folder for our server. So that if we’re required to send index.html from any of our route handler, we can just send index.html and our server will by default always send index.html present in dist. All this can be accomplished using fastify-static module. Add the code shown below in your server/server.js to see it in action yourselves.

Now let’s get rid of {msg : hello, world!} and host our react app on / route instead. It can be done very easily. We just need to replace
return {msg: hello, world! with reply.sendFile('index.html')

Now run your app using node server/server.js and if everything goes well you would see your react application up and running on http://localhost:3000 like shown below

Ok so we finally have something meaningful to see when our server starts but the problem of volatile changes still exists. Well, Now that’s because we have not yet updated our frontend, to make it interact with fastify server and take benefit of the database. In order to achieve that we just need to modify three files namely actions.js , reducer.js and menu.js . So let’s start by our actions file.

Step 2: Modify src/redux/actions/actions.js

In Here we’d be modifying readItems action creator to make it capable of fetching data from the database. Currently this action creator does nothing special than returning an object with type : READ we would be modifying it to return a function instead which would in return also dispatch some action creators but conditionally. But we know that normally an action creator can only return objects, So in order to override this traditional behaviour we would be using redux-thunk module. You have installed it already in Part 1 so no need to install it again. Until now it’s need didn’t arise so we didn’t use it but we will now. In case you never heard this module before, you can know about redux-thunk in great detail here. But in a nutshell it’s just a module which let’s us write action creators which can return functions (which in turn also dispatches actions creators but conditionally) instead of an conventional action. As we discussed few lines above the returned function would also be dispatching some action creators So, let us first see what these extra action creators actually are -

So what do we have here, we have three new action creators let’s study their functionality one by one.

  • fetchItemsBegin dispatches when we initiate fetching/loading data from database.
  • fetchItemsSucces dispatches when we have successfully fetched the data. And it is dispatched with a payload property which contains the items that were fetched.
  • fetchItemsFailure dispatches when any sort of failure occurs while fetching data from database. It also has a payload property which contains the errors.

These action creators have no importance on there own. They are not used independently rather they all are wrapped inside a function which dispatches them conditionally so let us discuss this function.

So here is our modified readItems action creator, which returns function as opposed to action object. The function in turn dispatches fetchItemsBegin in order to indicating that fetching has started, then we request data from server and depending on result dispatch either fetchItemsSuccess or fetchItemsFailure. If data is fetched successfully fetchItemsSuccess is dispatched (with fetched data as argument) or in case some failure occurs fetchItemsFailure is dispatched (with failures that occurred as argument). So that pretty much sums up what our modified readItems action creator does and looks like. You may find the complete actions.js here.
Now let’s modify reducer to handle the new action creators we just created.

Step 3: Modify src/redux/reducers/reducer.js

Let’s first import new action types which we need to handle.

Now how are we gonna handle them ? Okay so I’ll explain point wise how every action is going to be handled and then we’ll see all that in code. So here we go.

  • FETCH_ITEMS_BEGIN : nothing would change except loading would be set to true.
  • FETCH_ITEMS_SUCCESS : loading would be set to false and menuItems array would get filled with data received from server.
  • FETCH_ITEMS_FAILURE : loading would be set to false , menuItems array would be truncated and errors would be set to errors found.

Now let’s see how we implement all this via code.

Okay so by now, you must have understood how the the newly created actions are handled by the reducer both conceptually and by means of code. You may find the final version of reducer here.

Now we are only left with triggering of appropriate server endpoints by making appropriate HTTP requests from client side when we do one of the following Create data, Fetch data, Update data, Delete data. This can be accomplished with the help of a HTTP Client. So let’s get this done next up.

Step 4: Modify src/menu.js

HTTP client that we’ll use in this tutorial is axios (feel free to use fetch or any other HTTP client that you like, it hardly makes any difference). Now let’s import it at the top.

In order to read data from database, we just need to call the readItems action creator and nothing more. i.e. we don’t need to make any HTTP request,readItems does all by itself internally. We need to call readItems as soon as app loads or reloads, or in other words when component mounts so that our redux store get’s filled with data automatically as soon as app loads or reloads. Therefore we’ve placed it in componentDidMount life cycle method . Code for this is shown below.

In order to add data, we need to make HTTP POST request at /api/menuItems with data of new Item to be created and it sends response in form of added Item details. It is placed in handleAddItem handler, so that Item gets added to database as soon as person clicks Add button, let’s see code for this.

In order to modify a particular item having id equals to id,we need to make a HTTP PUT request at /api/menuItems/:id with the data needs to be corrected. In response server sends the copy of updated item. It is placed in handleUpdateItem handler, Code for this is shown below.

In order to delete a particular item having id equals to id. We just need to make a HTTP DELETE request at /api/menuItems/:id . In response it sends data of deleted Item back. It is placed in handleDeleteItem. Code is shown below.

That’s all that for our menu.js. You may find the final code of menu.js here.
We are pretty much done with interconnecting our front to backend. And if you run your application now you’ll see that data is persistent and it stays even after application is reloaded. Don’t forget to build you react app before serving it. You may use parcel build src/index.html to build your react app.
In case you experience some errors please refer to final code of this app present on GITHUB.
Okay, so now we are only left with Deployment process. If you want see how you can deploy any application having stack similar to this ,then please follow along.

Let’s make it Live

I’ll be using Heroku to deploy our application. In order to deploy any node app to heroku please follow these steps shown below.

  1. In your fastify/node server, ensure that your port is declared like this port = process.env.PORT || 3000 . If you are not using process.env.PORT then your application is likely not detect dynamic ports and would crash very often in dyanmic environments like heroku.
  2. Ensure that Git is added to the project. If not just execute git init and create a .gitignore file as well to ignore the data we don’t want to add to git. which in this case are /node_modules , package-lock.json , .cache
  3. Now in order to stage all the files run git add . and in order to commit run git commit -m "any message".
  4. I expect you to have heroku cli, if you don’t then please insall it before proceeding. After you have installed heroku cli execute the commands shown below.

Note : In case of some error, you can run heroku logs --tails. To what went wrong.

Now if all things went good, within few minutes you will be able to see your application live on web.

So that’all folks, I hope that you liked the two part tutorial series in which we built a Fullstack Fastify React Application completely from scratch, Through out this tutorial we covered various concepts like Optimistic updating, React Redux concepts, etc. I hope that you learnt some new things from the tutorial and I wish you luck for the future

If you liked this post, I’d be very grateful if you’d help it spread by sharing it to a friend, via Twitter or Facebook. And as always please let me know in comments how did you find this tutorial. Thank you!

--

--