Cloudinary Blog

Building a Roommate-Matching App With Cloudinary and Jamstack

By Marcelo Ricardo de Oliveira
Building a Roommate-Matching App With Cloudinary and Jamstack

Roommate matching can be a pain—especially during the COVID pandemic when people don't want to meet in person. Matching apps like Flatmates, Roomster, and roommates.com are helpful, and if you're in the roommate-matching space, you know that great video is essential for those seeking roommates. Fortunately, Cloudinary can help.

Cloudinary is a cloud-based SaaS with which you as developers can upload and manage videos in the cloud, editing, transforming, and optimizing them for any screen. You can then insert those videos into a website through a video-player app. This article shows you how to build a sample app for matching roommates by leveraging video capabilities.

Explaining the Roommate-Matching App

This sample Roommate-Matching App is based on a simplified use case of a typical apartment-and-room rental app: owners input details about their properties and room seekers might then signal interest. Before closing the deal, both parties exchange profiles, make an appointment, and meet at the location.

To avoid meeting in person during the pandemic, owners would upload a 10-minute video that showcases the room and the rest of the property. The video also talks about their hobbies, likes, and dislikes. To protect privacy, the video is restricted for access. To watch it, the room seeker must secure the owner's approval.

The following diagram illustrates the sequence of events within the app:

Action sequence

Understanding the Tech Stack

This app boasts a tech stack that’s ready for the modern web and rapid development:

  • Next.js for the user interface (UI): Next.js is a React-based framework that enables such functions as server-side rendering and static generation.

  • Netlify with Jamstack for hosting: Unlike traditional web apps, Netlify's simple architecture works on prebuilt files served over a content delivery network (CDN). Such an approach results in higher performance, security, and scalability along with lower costs and an enhanced developer experience.

  • Sanity as a storage back end: Given the app’s serverless scenario, Sanity Data Store provides data access and stores room details as a collection of documents. You'll use Sanity's JavaScript client library to conveniently encapsulate low-level operations.

  • Cloudinary for media management: For an optimal user experience, this app delivers optimized images and video streaming on Cloudinary, whose dashboard and tools offer an end-to-end solution for easily managing media assets.

Creating a Next.js App

This section steps you through the procedure for creating the sample Roommate-Matching App with the tech stack described earlier. For brevity, the description also contains code samples from the key parts of the app, emphasizing those sections that use the Cloudinary API.

  1. Create a GitHub repository and then install Node.js and the Next.js command-line tool.

  2. Create a standard Next.js project:

    create-next-app

  3. Open the project in a code editor and create a Next.config.js file that contains this snippet:

    Copy to clipboard
    module.exports = {
    distDir: "dist",
    target: "serverless"
    }
  4. Build and test on localhost with the commands npm install, npm run build, and npm run dev; and then go to http://localhost:3000. Commit the local changes and push them to GitHub:

Copy to clipboard
git commit
git push

Next.js app

Deploying the App to Netlify

Now deploy your app to the Netlify cloud for hosting:

  1. Sign up for a free Netlify account and then create a Netlify site from your GitHub repository by clicking New Site from Git. Choose the repository you want to link to your site on Netlify. When you push to Git, Netlify will run your build tool of choice on its servers and deploy the result. Change the build settings on your dashboard to read like this:

    Copy to clipboard
    Build command: `next build`
    Publish directory: `dist/`
  2. Click the Plugins tab and add Next to the Netlify plugin. That plugin will then build and deploy your Next.js app with no other configurations required.

  3. From the Deploy tab for your site, force a new deployment, and test the deployed website: https://YOUR-SITE-NAME.netlify.app/.

    Deploy Netlify

    Your Netlify project is now ready for continuous integration: every time you push a commit, Netlify will connect to your app repository and automatically publish your website.

  4. Open your Netlify dashboard, view the Functions tab, and note that Netlify has automatically exported the Hello API endpoint as a serverless function at https://YOUR-SITE-NAME.netlify.app/.netlify/functions/next_api_hello, which gives you the same result as the http://localhost:3000/api/hello endpoint:

    {"name":"John Doe"}

Every time your app calls the /api/hello endpoint, Netlify will redirect it internally to the /functions/next_api_hello path of your deployed website.

Recall that you created the Next.config.js file with this code before:

module.exports = { distDir: "dist", target: "serverless" }

When Netlify builds your app with the next build command, the settings above cause Next.js to prepare a serverless deployment, which splits your app into small parts, called lambdas. Here’s how Next.js builds and deploys pages and APIs to the distribution (dist) folder:

  • /pages/index.js => /dist/serverless/pages/index.js
  • /pages/api/rooms/[id].js => /dist/serverless/pages/api/rooms/[id].js

Despite having the same name, the target files in the dist folder are different from their source files because each lambda file contains a complete bundle that can run without dependencies. When you deploy your app as a serverless website in Netlify, those lambdas greatly improve the app’s reliability and scalability.

Implementing Netlify Identity

Netlify Identity’s convenient authentication service is easy to integrate into your app. Follow these steps:

  1. Open the Identity tab of your Netlify project and click Enable Identity.

  2. Install on the command line the Netlify Identity Widget, Bootstrap, React Bootstrap, and FontAwesome with npm:

    Copy to clipboard
    npm install netlify-identity-widget
    npm install bootstrap
    npm install react-bootstrap
    npm install fontawesome
  3. Add the Layout and Header components to the /components folder of your project:

    Copy to clipboard
    /components/
    /Layout.js
    /Header.js
  4. Add the netlify-identity-widget script below to the Layout component:

    Copy to clipboard
    return (
    <>
      <Head>
        <title>Your Roommate Matching App</title>
        <script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
      </Head>
    .
    .
    .
      <Header />
      <main>
        <link rel="stylesheet" type="text/css" href="../app.css" />
        <div className="container">{children}</div>
      </main>
  5. Add the data-netlify-identity-menu component below to the Header component:

    Copy to clipboard
    <div data-netlify-identity-menu></div>
  6. In the Index page, wrap the UI with the Layout component:

    Copy to clipboard
    return (
    <Layout>
        [...]    
    </Layout>
  7. Run the app again, click the Sign Up link in the Netlify Identity menu, and register for a new account.

  8. Open your email to confirm the registration and then log in to the new account:

netlify-identity
netlify-identity login

Accessing Data With Sanity

For data access, outsource your data store to Sanity. Integrate Sanity into the app, as follows:

  1. Create two folders, name them /public/img and /data, and then add some pictures of a room for rent under /public/img. In the data folder, add a dummy JSON file called data.js with information on the room.

  2. Edit the index page to display the room-catalog data from a static JSON file.

  3. Sign up for a free Sanity account. Create a Sanity project and then add a collection named rooms.

  4. Install the @sanity/client npm package.

  5. Create a .env file and add the credentials of your Sanity project to it:

    Copy to clipboard
    SANITY_PROJECT_ID=XXXXXXXXXX
    SANITY_API_TOKEN=xxxxxxxxxxxxxxxx
  6. Edit the Next.config.js file to export your Sanity credentials:

    Copy to clipboard
    const sanityProjectId = process.env.SANITY_PROJECT_ID;
    const sanityApiToken = process.env.SANITY_API_TOKEN;
    module.exports = {
    distDir: 'dist',
    target: 'serverless',
    publicRuntimeConfig: {
    sanityProjectId: sanityProjectId,
    sanityApiToken: sanityApiToken
    }
    }
  7. Go to the Environment Variables section of your Netlify project settings and add the same Sanity credentials from the Next.config.js file.

  8. Create the Pages/API/rooms/[id].js endpoint with the GET and POST methods to connect to your Sanity data store with your credentials and perform data operations on the rooms collection. Within your API methods, use the Sanity API JavaScript client and call the getDocument, fetch, and createOrUpdate methods of the Sanity client to get a single room document, to retrieve all the room documents, and to update a room document, respectively.

  9. Build, run, and test the new API endpoint on localhost.

  10. Install the SWR React Hooks library package for fetching and caching remote data:

    Copy to clipboard
    >npm i swr
  11. Edit the index page to fetch room data from the new API endpoint instead of the static JSON file.

  12. Run the app again. The room information along with images are now displayed on the homepage, for example:

Available rooms

Integrating Image and Video Services With Cloudinary

Media access is the last piece in the puzzle. Follow the steps below to integrate your app with Cloudinary’s services and components.

1. Sign up for a free Cloudinary account and create a Cloudinary project. Click Settings > Upload and, under Upload presets, look for the preset with the Mode = Unsigned setting.

2. Click Settings > Security and, under Restricted media types, deselect the Resource list option.

3. Add your Cloudinary project’s credentials to the .env file:

Copy to clipboard
CLOUDINARY_CLOUD_NAME=XXXXXX
CLOUDINARY_UPLOAD_PRESET=XXXXXX
SANITY_PROJECT_ID=XXXXXXXXXX
SANITY_API_TOKEN=xxxxxxxxxxxxxxxx

4. Edit the Next.config.js file to export your Cloudinary credentials.

5. Add the following section under module.exports in the Next.config.js file:

Copy to clipboard
const cloudinaryCloudName = process.env.CLOUDINARY_CLOUD_NAME;
const cloudinaryUploadPreset = process.env.CLOUDINARY_UPLOAD_PRESET;
const sanityProjectId = process.env.SANITY_PROJECT_ID;
const sanityApiToken = process.env.SANITY_API_TOKEN;
module.exports = {
  distDir: 'dist',
  target: 'serverless',
  publicRuntimeConfig: {
    cloudinaryCloudName: cloudinaryCloudName,
    cloudinaryUploadPreset: cloudinaryUploadPreset,
    sanityProjectId: sanityProjectId,
    sanityApiToken: sanityApiToken
  },
  images: {
    domains: ['res.cloudinary.com']
  }
}

6. Add the Cloudinary script below to the Layout.js file:

Copy to clipboard
[…]
  return (
    <>
      <Head>
        <style>{dom.css()}</style>
        <title>Your Roommate-Matching App</title>
        <script src="https://widget.cloudinary.com/v2.0/global/all.js" type="text/javascript"></script>
[…]

7. Add to the Netlify settings your Cloudinary credentials as environment variables with the same names and values as those declared in the Next.config.js file.

8. Replace the <img> tag with the <Image> component from next/image to eliminate the layout-shift effect on pages that contain images.

9. Create button components to enable users to upload, play, request access to, and approve requests for videos.

10. Modify the upload-button component (the UploadButton.js file) to open the Cloudinary upload widget and call the API endpoint to update the room’s image and video URLs:

Copy to clipboard
    var myWidget = cloudinary.createUploadWidget({
      cloudName: publicRuntimeConfig.cloudinaryCloudName,
      upload_preset: publicRuntimeConfig.cloudinaryUploadPreset,
      showAdvancedOptions: true
    }, (error, result) => {

      if (result.event == "success") {

        console.log(result.info);

        if (result.info.resource_type == "image") {
          fetch('/api/rooms/' + room._id, {
            method: 'POST',
            body: JSON.stringify({ pic: result.info.secure_url }),
            headers: {
              'Content-Type': 'application/json'
            },
          })
          .then(res => mutate('/api/rooms/' + room._id));
        }

        if (result.info.resource_type == "video") {
          fetch('/api/rooms/' + room._id, {
            method: 'POST',
            body: JSON.stringify({ videoId: result.info.public_id }),
            headers: {
              'Content-Type': 'application/json'
            },
          })
          .then(res => mutate('/api/rooms/' + room._id));
        }
      }
      else {
        console.log(error);
      }
    })

    myWidget.update({tags: ['room-' + room._id]});
    myWidget.open();
  }

Note
On Cloudinary’s free plan, the maximum image-file size you can upload is 100 MB. For all the upload limits, see the right-hand side of your account settings page.

11. Create a new Details page under the pages/details/[id].js file and add to the page a component named VideoPlayer so that users can watch video and review video requests.

12. Add Cloudinary’s embedded video player to the VideoPlayer.js file:

Copy to clipboard
import getConfig from 'next/config';

const { publicRuntimeConfig } = getConfig();

const VideoPlayer = (props) => {

  const videoUrl = 'https://player.cloudinary.com/embed/'
  + '?cloud_name=' + publicRuntimeConfig.cloudinaryCloudName
  + '&public_id=' + props.room.videoId
  + '&fluid=true&controls=true&source_types%5B0%5D=mp4';

  return <iframe
    src={videoUrl}
    height="400"
    allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
    allowFullScreen
    frameBorder="0"></iframe>
}

export default VideoPlayer

Walking Through the Roommate Matching App

Below is how the app works.

  1. The owner opens the homepage and, if rooms available for rent are displayed, logs in with the user name and password previously registered with Netlify Identity:

    login to app

  2. When users log in, they can upload videos for the rooms they own by clicking Upload Media:

    Available rooms

  3. Cloudinary’s upload widget uploads the media (images or videos) for a specific room:

    Upload widget

  4. The app automatically assigns the media to the room in question.

    Upload Queue

  5. The API’s POST method in the app accesses the rooms collection of the Sanity data store and updates the image and video URLs in the current room’s document.

At this point, the owner can click the play button to verify that the video is in good shape (see the screenshot below). This details page is powered by Cloudinary’s cloud-hosted player, which you can conveniently embed in an <iframe> element.

house tour

Now that the video has been uploaded, the room seeker can log in to the app with his or her Netlify Identity credentials:

roommate app login

Afterwards, the Request Video button appears for the room with video uploaded by its owner. The room seeker can then click the button to request permission to watch.

Request video

The app then displays the Request Pending button:

Request pending

Since the match event is an agreement between two parties, the room seeker must wait till the room owner opens the room-details page again, takes note of the request, and approves it by clicking Approve:

Approved listing

Afterwards, the app displays the Watch Video button on the room seeker’s page:

Watch video

By clicking Watch Video, the room seeker is redirected to the room-details page to watch the video:

Click watch video

Moving on to the Next Steps

The Roommate-Matching App described above demonstrates how to easily configure and integrate frameworks, libraries, and platforms, such as Next.js, Netifly, Sanity, and Cloudinary, to build robust, cloud-based solutions. In particular, by integrating Cloudinary’s first-class APIs and components with your app, you can enable room owners to quickly upload, retrieve, and manage media assets as helpful details for room seekers in quest of the ideal match.

Feel free to use the code and API methods you learned above to integrate video into other apps. To leverage the Cloudinary platform to effectively manage media assets for your apps, first sign up for a free account.

Here are more resources offered by Cloudinary:

Also, check out the serverless architecture of Next.js.

To obtain the source code of the sample app, download or fork the GitHub repository.

About the Author

Marcelo Ricardo de Oliveira is a senior freelance software developer who lives with his lovely wife, Luciana, and his little buddy and stepson, Kauê, in Guarulhos, Brazil. He is the cofounder of the Brazilian TV Guide and currently works for Alura Cursos Online.

Recent Blog Posts

Get Your Media Moving Faster with Cloudinary’s Media Optimizer

So, your boss comes to you in a panic: he's just heard about Google's Core Web Vitals initiative and needs you to optimize the company website right now! "No problem," you say, hiding your fear that it's not something that can be done overnight. Just taking the first metric, Largest Contentful Paint (LCP), how can you possibly identify all the large elements - most likely images or video posters - of the many hundreds of pages that make up your site? There are already thousands of high-resolution (read massive) media files stored away, which marketing could use any time. How are you going to make sure they're all compressed to a size small enough to be delivered within the threshold? Not to mention all the new images and videos that will be created over time...

Read more
How to Tap Into the Value of User-Generated Content (UGC)

User-generated content (UGC) took off with, first of all, the advent of the internet and, subsequently, social networks. Everyday consumers were given keys to the kingdom, so to speak, so that they, too, could compose and post content, simultaneously engaging with others online. Twitter, Facebook, Instagram, Snapchat, TikTok—the networks through which we can create and publish content have grown exponentially, and brands are becoming aware of the benefits of tapping into the gold mines offered by those networks.

Read more
Identifying Countries by IP Address in Columnar Databases Through SQL

Cloudinary reaps a myriad of open web traffic, from ad networks to e-commerce sites. Our Data Science team is dedicated to analyzing the data for use internally and externally.

A glance at any General Data Protection Regulation (GDPR) article would reveal that—unlike Android device IDs (AID), through which users can reset their web address—keeping user identifiers, such as Internal Protocol (IP) and Media Access Control (MAC) addresses, as well as International Mobile Equipment Identity (IMEI), violates privacy. As a solution, you can discard all privacy identifications or make them visible to users for reset.

Read more
Digital-First Asset Management Explained

As the world changes, so does technology. I don’t need to name more than a handful of antiquated technologies before you nod in agreement: floppy disks, Walkmans, phone booths, VHS tapes, each of which have been phased out or rendered useless by new solutions that meet the same need but much more effectively.

Read more
How to Build Workflows With Cloudinary’s MediaFlows

Many of you who work with the Cloudinary platform have a media-associated workflow for moderation of images, dispatch of notifications with certain data or headers, implementation of activities through add-ons, etc. For most of those cases, Cloudinary would suggest that you take advantage of our webhook notifications and build the workflow with an infrastructure like AWS Lambda. This post describes how to do that with Cloudinary’s MediaFlows a beta product that helps tackle management and operational tasks related to visual media.

Read more