How to Integrate Cloudinary With React and GraphQL

In this lesson, we'll pick up from what we've done in the previous lesson by modifying our server code to utilize Cloudinary to host listing images for our TinyHouse application.

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 \newline Pro subscription or 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

To use the Cloudinary API in our Node Server application, we'll need to first install the relevant Cloudinary package. We'll head over to our terminal and install the NPM Cloudinary library for our server project as a dependency. The community has recently introduced a declaration file directly to the Cloud inary package, so we won't have to install any additional typings. Just like how we've set up dedicated files for Stripe and Google to prepare functions to interact with their APIs, we'll create a Cloudinary file in our libapi folder that will be responsible in having the function needed to interact with the Cloudinary API. And in the libapi index file, we'll re-export the soon-to-be-created Cloudinary object. In this Cloudinary file, we'll import the Cloudinary module and we'll export an object we'll create called Cloudinary. This object will have an asynchronous function labeled upload that will expect an image argument of type "shrink". We're going to expect that in our Resolver function, when we plan to host a listing, that function will call this upload function and pass the listing base64 encoded image. This function will then use the Cloudinary API to upload that image as an asset to our Cloudinary dashboard. And when uploaded, we'll also want this function to finally return the uploaded image URL. We can upload an image with the upload function available in the Cloudinary module. It's a little verbose, but it can appear something like this, Cloudinary.v2.uploader.upload. And this is an asynchronous function where we'll be interested in the response that is to be returned. The upload function takes three arguments, where we're only going to be using the first two. First argument is the file that's going to be uploaded, which will be the image we're going to pass in. The second argument is an options object. In the options object, we can specify options and configuration for how we want the upload to happen. For example, we can specify the folder we'll like to upload the image in on our Cloudinary dashboard. This isn't a requirement, but it's helpful in helping organize our Cloudinary Cloud server. You can upload an image to any folder you might be interested in, and upload it to a folder labeled "teach_assets". To upload to our own Cloudinary dashboard, we'll need to specify the credentials of our account within this options object . We saved the Cloudinary environment credentials of our account in our server.n file in the last lesson, so we'll specify and label them here. We'll declare the API key option and pass the Cloudinary key environment variable as the value. We'll declare the API secret option and pass the Cloudinary secret environment variable. And we'll declare the Cloud name option and pass the Cloudinary name environment variable. And this should pretty much do the upload the way we want it to. There are probably other options we can specify, but this is the only thing we care about. We just want to upload the listing image onto our Cloudinary dashboard and onto a certain folder. The only other thing we'll want to do in this function is return the URL of the uploaded image. Here's some Cloudinary documentation that will link in the lesson manuscript that tells us the sample response we'll get from an upload. There's a lot of different things we can look to access, but we're interested in simply retrieving the URL of the uploaded image. There's a URL and secure URL fields. We'll go with attempting to access and return the secure URL field just in case Cloudinary is dictating that this field is perhaps more secure. And finally, ESLint is telling us that our identifiers should be in camel case, but we'll keep it as is since we have to, so we'll disable the camel case rule of our TypeScript ESLint plugin just within this function. Great. We'll now look to update the host listing mutation. In our listing resolvers map, we'll import the Cloudinary object from the lib API folder. In our host listing mutation resolver function, we'll look to use the upload function within our Cloudinary object instance. Right before we insert a new document to the listings collection, we'll call the upload function and pass the base64 encoded image along. We'll obtain the result of this function as a constant labeled image URL, and in our document that we're planning to insert into the collection, we'll specify that the image field will now have a value of this image URL we 've obtained. And that's pretty much it. When we now create a new listing in our application, the image of that listing should reference the URL image in our Cloudinary dashboard. Let's see this in action. With both our server and client applications running, we'll head to the host page of our client app in attempt to create a new listing. We'll say house, maximum number of guests of four, Belair Mansion, or large Belair Mansion, Modern and Clean and Large for the description, the address will say 251 North Bristol, LA, California, 90210 as the zip code for the image we'll use the image we had before, and for the price will say $200 per day. Click submit, we'll see the loading indicator telling us we're creating the listing. The listing has been created. This looks pretty much identical to the listing we've created before, but now let's take a look at what this image source is. We'll go to our document inspector, try to find the source of the image, and we can see that the image is now referenced from our Cloudinary dashboard. If we head to the dashboard of our Cloudinary account, we go to the media library section, there'll be a new folder called th_assets if it didn't exist before, and by launching this particular folder, we'll see the image upload that Cloudinary has made and kept within this particular folder. At this moment in time, Cloudinary offers a variety of different things we can do with this image. We can customize it, edit them, enlarge them, shrink them, etc. But for us, our purpose has been complete. We just wanted to host all our listing images outside of our database and simply in a storage where we can retrieve the URL. And if we go to the Mongo Atlas dashboard, we'll see the two listings we've created so far. Assuming we don't want to have any listings where the image references the base 64 value, let's delete the previous listing we've created before. If we head back to the tiny house application, we go to our profile page, and at this moment we'll see the only listing we've just recently created. Fantastic. Note that the request payload limit we've specified in the root index file of two megabytes is something we'll keep. We do require the user to upload an image of at least under one megabytes, so this should cover pretty much all the requests that can be made in our application. Now, if you wanted to loosen the requirements for the image size to be uploaded from the client to the server, before we interact with the Cloudinary API, you can increase this limit as you see fit. [BLANK_AUDIO]