Airbnb clone, use the database instead of the file

Join the 2022 Full-Stack Web Dev Bootcamp!


This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.

Now that we created the model that uses the DB to serve the data, it’s time to use it!

First, I’m going to manually add the houses data we had in the file to the database, creating 2 rows in the houses table:

Now, the houses.js file is imported by 2 files:

  • pages/houses/[id].js
  • pages/index.js

We need to change this reliance on the file, and use a new API instead.

Let’s work on the list page first.

Houses list

Instead of loading the houses from the JSON file, we’re going to use the House model.

We can use the model inside the getServerSideProps() method. Next.js becomes an hybrid between frontend and backend.

Let’s start from pages/index.js.

We can import the model:

import { House as HouseModel } from '../model.js'

Then in getServerSideProps() we get the houses using:

const houses = await HouseModel.findAndCountAll()

and we return the value iterating over the results, getting the dataValues property, which returns a plain JS object with the data we need:

return {
  props: {
    nextbnb_session: nextbnb_session || null,
    houses: houses.rows.map((house) => house.dataValues)
  }
}

Now we get houses as a prop in the Home component:

export default function Home({ nextbnb_session, houses }) {
  //...

and we need to do a little refactoring of how we print the JSX. We need to embed the content inside the JSX, otherwise it can’t access the value of the prop, like this:

import House from '../components/House'
import Layout from '../components/Layout'
import Cookies from 'cookies'
import { useStoreActions } from 'easy-peasy'
import { useEffect } from 'react'
import { House as HouseModel } from '../model.js'

export default function Home({ nextbnb_session, houses }) {
  const setLoggedIn = useStoreActions((actions) => actions.login.setLoggedIn)

  useEffect(() => {
    if (nextbnb_session) {
      setLoggedIn(true)
    }
  }, [])

  return (
    <Layout
      content={
        <div>
          <h2>Places to stay</h2>

          <div className="houses">
            {houses.map((house, index) => {
              return <House key={index} {...house} />
            })}
          </div>

          <style jsx>{`
            .houses {
              display: grid;
              grid-template-columns: 49% 49%;
              grid-template-rows: 300px 300px;
              grid-gap: 2%;
            }
          `}</style>
        </div>
      }
    />
  )
}

export async function getServerSideProps({ req, res, query }) {
  const cookies = new Cookies(req, res)
  const nextbnb_session = cookies.get('nextbnb_session')
  const houses = await HouseModel.findAndCountAll()

  return {
    props: {
      nextbnb_session: nextbnb_session || null,
      houses: houses.rows.map((house) => house.dataValues)
    }
  }
}

The house detail

We do the same in pages/houses/[id].js.

We import the model:

import { House as HouseModel } from '../../model.js'

Then we use it to get the house by id:

const house = await HouseModel.findByPk(id)

and we can return its dataValues in the return object:

return {
  props: {
    house: house.dataValues,
    nextbnb_session: nextbnb_session || null
  }
}

In this case we were already returning a house prop, so nothing needs to be changed.

Here is the full source code:

import Head from 'next/head'
import Layout from '../../components/Layout'
import DateRangePicker from '../../components/DateRangePicker'
import { useState, useEffect } from 'react'
import { useStoreActions } from 'easy-peasy'
import Cookies from 'cookies'
import { House as HouseModel } from '../../model.js'

const calcNumberOfNightsBetweenDates = (startDate, endDate) => {
  const start = new Date(startDate) //clone
  const end = new Date(endDate) //clone
  let dayCount = 0

  while (end > start) {
    dayCount++
    start.setDate(start.getDate() + 1)
  }

  return dayCount
}

export default function House({ house, nextbnb_session }) {
  const [dateChosen, setDateChosen] = useState(false)
  const [numberOfNightsBetweenDates, setNumberOfNightsBetweenDates] =
    useState(0)

  const setShowLoginModal = useStoreActions(
    (actions) => actions.modals.setShowLoginModal
  )

  const setLoggedIn = useStoreActions((actions) => actions.login.setLoggedIn)

  useEffect(() => {
    if (nextbnb_session) {
      setLoggedIn(true)
    }
  }, [])

  return (
    <Layout
      content={
        <div className="container">
          <Head>
            <title>{house.title}</title>
          </Head>
          <article>
            <img src={house.picture} width="100%" alt="House picture" />
            <p>
              {house.type} - {house.town}
            </p>
            <p>{house.title}</p>
          </article>
          <aside>
            <h2>Choose a date</h2>
            <DateRangePicker
              datesChanged={(startDate, endDate) => {
                setNumberOfNightsBetweenDates(
                  calcNumberOfNightsBetweenDates(startDate, endDate)
                )
                setDateChosen(true)
              }}
            />

            {dateChosen && (
              <div>
                <h2>Price per night</h2>
                <p>${house.price}</p>
                <h2>Total price for booking</h2>
                <p>${(numberOfNightsBetweenDates * house.price).toFixed(2)}</p>
                <button
                  className="reserve"
                  onClick={() => {
                    setShowLoginModal()
                  }}
                >
                  Reserve
                </button>{' '}
              </div>
            )}
          </aside>

          <style jsx>{`
            .container {
              display: grid;
              grid-template-columns: 60% 40%;
              grid-gap: 30px;
            }

            aside {
              border: 1px solid #ccc;
              padding: 20px;
            }
          `}</style>
        </div>
      }
    />
  )
}

export async function getServerSideProps({ req, res, query }) {
  const { id } = query
  const cookies = new Cookies(req, res)
  const nextbnb_session = cookies.get('nextbnb_session')
  const house = await HouseModel.findByPk(id)

  return {
    props: {
      house: house.dataValues,
      nextbnb_session: nextbnb_session || null
    }
  }
}

You can now remove the houses.js file, but before doing so, add the data to the database:

TIP: In the owner field, add the id of a user, like 1 for example. This associates the house to a user in our database.

See the code on GitHub

Next part: Handle bookings

Want to become a better Web Developer? Join the 2022 Web Development Bootcamp!

⭐️⭐️⭐️ Join the 2022 Web Development Bootcamp ⭐️⭐️⭐️