How to Migrate a React App From MongoDB to PostgreSQL

In this lesson, we'll convert the server of our TinyHouse project to use PostgreSQL & TypeORM.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two
  • [00:00 - 00:11] In this lesson, we'll integrate Postgres and type ORM into our tiny house application. To begin, clone the source code from Lesson 15.6 from Part 2 of the course.

    [00:12 - 00:37] First, let's head over to our client application and make sure our npm packages are installed by running npm install. And that's it for our React client.

    [00:38 - 00:57] All the further changes we need to make in order to integrate Postgres and type ORM into Tinyhouse are on the server side. Let's head over to the node server and make sure our npm packages are installed by running npm install.

    [00:58 - 01:21] Next, we'll make sure our npm packages are updated by running npm update. If npm warns about any vulnerabilities from our packages, we also want to make sure we fix those by running npm audit fix.

    [01:22 - 01:55] Since we're using type ORM and Postgres instead of MongoDB, we'll be uninst alling the MongoDB drivers. Like what we did in the previous lesson, we'll first install the packages type ORM requires by running npm install type ORM and reflect metadata.

    [01:56 - 02:15] Next, we'll install the Postgres database driver by running npm install PG. Next, we'll enable the following settings in TS config.

    [02:16 - 02:25] We'll also want to create a new .env file with the following variable set. Note, since we're not changing anything else in our stack, we are only swapping MongoDB with Postgres.

    [02:26 - 02:34] You can use the same variables you use when going through Part 2 of the course. Of course, minus dbuser, dbuser password and db cluster.

    [02:35 - 02:44] Next, let's copy the same ORM config from our previous lesson and drop it into our tiny house server. Note, these are my config values, so yours might look different.

    [02:45 - 03:04] The one last thing we need to do is to create a new Postgres database. Let's open PG admin and create a new database called tinyhouse.

    [03:05 - 03:20] And then we'll update ORM config with our new database name. Like what we did in the previous lesson, we'll first need to import reflect metadata in the beginning of our code.

    [03:21 - 04:19] Next, we'll head over to our database folder and modify our connect database function to use type ORMs, create connection method, to connect our Mongo server to our Postgres database. Next, we'll create a new folder called entities to define our database schema.

    [04:20 - 04:38] In this folder, we'll create an export booking entity, listing entity, and a user entity. When we're building our entities, we can use our TypeScript definitions as a starting point, since they're almost identical.

    [04:39 - 09:11] The only difference is that our listing and user TypeScript definitions have a authorized property, whereas our database equivalent does not. To see a full list of data types, type ORM supports for each database system, head over to their official documentation.

    [09:12 - 11:05] Next, we'll modify our TypeScript definitions to match our database schema. Instead of using MongoDB's collection type, we'll use Type ORM's repository type.

    [11:06 - 12:46] Instead of using MongoDB's underscore ID, we'll just use ID. Before we start modifying our GraphQL resolvers to use Type ORM instead of MongoDB, let's see our Postgres database with some data.

    [12:47 - 12:57] Note, please obtain a copy of the c.es file from the provided source code. The c data was modified to remove MongoDB's object ID usage.

    [12:58 - 13:16] Instead of using MongoDB's insert1 method, we'll use Type ORM's create method to create a new instance of an entity locally and then use the save method to save it to our database. Let's also update our clear script just in case if we want to reset our database.

    [13:17 - 13:46] Instead of using MongoDB's drop method, we'll use Type ORM's clear method instead. Let's run our seed function using npm run seed.

    [13:47 - 14:04] And we can see our seeded data in our tiny house database from the PGNM application. Finally, let's modify our GraphQL resolvers to use Type ORM.

    [14:05 - 14:16] Let's start off by updating our query.user resolver. We don't need to do anything to our Find1 method because MongoDB and Type ORM's API for Find1 are identical.

    [14:17 - 14:26] We are also going to cast the user constant as our TypeScript user type. This is done so that we can assign an authorized property to it later on in the code.

    [14:27 - 14:42] Since our Postgres table uses an ID column instead of an underscore ID field, we'll change that across the entire file as well. This also means that we can remove our custom user.id resolver.

    [14:43 - 14:52] Next, our user.bookings resolver. Instead of using MongoDB's Find method, we'll use Type ORM's FindByID's method.

    [14:53 - 15:40] Type ORM's FindByID's method takes an optional parameter for skip and take, which works similarly to MongoDB's skip and limit. [silence] Likewise, for our user.listings resolver, we'll be using Type ORM's FindByID's method with the skip and take optional parameters.

    [15:41 - 16:09] [silence] Type ORM has additional options for their Find methods. To learn more, check out their official documentation. Let's navigate to our viewer resolvers.

    [16:10 - 16:20] First, we'll replace any MongoDB's underscore ID with ID. We'll also delete the viewer.id resolver since we're not using underscore ID anymore.

    [16:21 - 16:37] Next, let's modify our login via Google function. Instead of using MongoDB's Find1 and Update method, we'll use Type ORM's Save method to save any entity changes to our database.

    [16:38 - 18:57] [silence] [silence] [silence] [silence] [silence] [silence] We'll also use Type ORM's Create method to create a new instance of an entity locally and then use the Save method again to save it to our database. [silence] [silence] [silence] Next, let's modify our login via cookie function. Instead of using MongoDB's Find1 and Update method, we'll use Type ORM's Find1 method to find our viewer from our database.

    [18:58 - 19:41] Then we'll use Type ORM's Save method to save any changes we made to our database. [silence] Next, let's modify our Connect stripe and disconnect stripe mutation resolvers.

    [19:42 - 20:15] For both of these resolvers, instead of using MongoDB's Find1 and Update method , we'll use Type ORM's Save method to save any changes we made to our database. [silence] We must also modify our authorized function to return a user entity so that we can use Type ORM's Save method elsewhere in the code.

    [20:16 - 21:29] [silence] [silence] [silence] [silence] Next, let's update our query.listing resolvers. Since our Postgres table uses an ID column instead of an underscore ID field, we'll change that across the entire file as well. This also means we can remove our custom Listing.ID resolver.

    [21:30 - 21:40] We don't need to do anything to our Find1 method because MongoDB and Type ORM's API for Find1 are identical. We're also going to cast the Listing constant as our TypeScript Listing type.

    [21:41 - 21:54] This is done so we can assign an authorized property to it later on in the code . Next, for our Listing's query resolver, here we're going to introduce Type ORM 's Order option for their Find methods.

    [21:55 - 22:26] We'll also need to create a new Order TypeScript definition for our Order variable like so. [silence] This Order API can also be found in their official documentation.

    [22:27 - 23:15] [silence] [silence] We'll also utilize Type ORM's count method to return the total number of List ings that match our Wear query. This is used by our React application to compute the total number of pages for pagination.

    [23:16 - 24:10] [silence] Type ORM's Find method takes an optional parameter for Order, Skip, and Take, which works similarly to MongoDB's sort, Skip, and Limit. [silence] Next, let's modify our Host Listing mutation resolver.

    [24:11 - 25:13] [silence] Instead of using MongoDB's Object ID method, we'll use Node's crypto library instead. [silence] Instead of using MongoDB's Insert1 function, we'll use Type ORM's Create method to create a new instance of an entity locally, and then use the Save method to save it to our database.

    [25:14 - 26:21] [silence] Next, for our Listing dot bookings resolver, instead of using MongoDB's Find method, we'll use Type ORM's Find by IB's method. [silence] We'll use the optional parameter Skip and Take to enable pagination.

    [26:22 - 26:39] Indexes support the efficient execution of queries in the table. Without indexes, Postgres must scan through every row of data to select the record that matches the query statement. If an appropriate index exists for a query, Postgres can use the index to limit the number of records to check.

    [26:40 - 26:58] For our MongoDB database, we created a Compound index using the Country, Admin, and City fields for our Listings collection. For our Postgres database, we'll do something very similar. We'll create a Multi-Column index from our Listings table using the following SQL statement.

    [26:59 - 27:37] [silence] Next, let's update our Create Bookings mutation resolver. Instead of using MongoDB's Object ID method, we'll use Node's Crypto library instead. Since our Postgres table uses an ID column instead of an Underscore ID field, we'll change that across the entire file as well.

    [27:38 - 28:25] This also means we can remove our custom bookings.id resolver. [silence] [silence] Instead of using MongoDB's Insert1 method, we'll use Type ORMs Create method to create a new instance of an entity locally and then use the Save method again to save it to our database.

    [28:26 - 30:21] [silence] [silence] Instead of using MongoDB's Update 1 method, we'll simply use Type ORMs Save method to save any entity changes to our database. [silence] [silence] Finally, let's test out our Postgres database and type ORM integration. First, we'll start our Node server by running npm start in the Server directory.

    [30:22 - 30:38] Then we'll start our React client by running npm start in the Client directory. First, let's make sure our Location search, pagination, and price sorting works .

    [30:39 - 31:26] [silence] Next, let's make sure our Google Sign-In works. [silence] We'll also want to make sure our Stripe Connect works.

    [31:27 - 32:25] [silence] Let's also make sure our Host Listing functionality works. [silence] [silence] Next, let's make sure our Book Listing functionality works.

    [32:26 - 32:52] I'll make a booking on a listing I created using another Google account. Here we can see the days I just booked are disabled. Perfect. Let's head over to the profile page where we can see the bookings I just made.

    [32:53 - 33:07] I'll sign in using my other Google account just to make sure the income changes when someone books one of your listings. Perfect. We just successfully removed MongoDB from our stack and added Postgres and type ORM.

    [33:08 - 33:09] you