Cloudinary Blog

Building a Roommate-Matching App With Cloudinary and Jamstack

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

Overlaying Glasses Or Masks on Avatars With Vue.js and Cloudinary

Deep learning, a subset of machine learning, breaks down tasks in ways that make all kinds of machine assists possible. With deep learning, you can identify images by machine, i.e., instead of hand-coding software with specific instructions for a task, you train the machine with large amounts of data and algorithms that enable it to learn how to perform the task.

Read more
On-Demand Viewing of Live Video Presents New Opportunities

In early 2020, Cloudinary was planning its fourth annual ImageCon conference, a two-day event in the heart of San Francisco, where we’d congregate with curious digital-media minds to brainstorm best practices for media management. Instead, the COVID-19 pandemic forced the entirety of ImageCon 2020 online. As with all other events being planned, we had to overhaul the content to be communicated on video. Gratifyingly, we found the right partner—the event platform Bizzabo—to turn that into a reality.

Read more
Why the Future of E-commerce Is Live

In a previous post, I discussed how “going live” is gaining popularity across industries and verticals. What began as a way for gamers to jam together has evolved into a medium for broader entertainment and business purposes. To continue the conversation, this post unpacks the current trends of shoppable live streams to shine a light on how brands are leveraging “lives” to connect with shoppers in new ways.

Read more
An Overview of Live-Streaming Video Trends

“Let’s go live.” For decades, that’s what newscasters say as they cut to real-time footage of a colleague reporting in the field. The live-video feed adds visual interest and perspective to a story beyond what can be communicated by someone sitting behind the news desk. In the same way, live-streaming video nowadays adds context to other consumer environments. From gaming and events to shopping and social media, “going live” enhances everyday experiences, and it’s something anyone can do with relative ease.

Read more