Create a restaurant menu in NextJS using Xata and Cloudinary
Tags: xata, cloudinary, frontend, serverless
Published: 2022-11-24 16:33:35

---
title: Create a restaurant menu in NextJS using Xata and Cloudinary
published: true
description: You'll be exploring the rich capabilities of Xata, a serverless database with built-in powerful search and analytics, and Cloudinary, a cloud-based image, and video management service.
tags: #xata #cloudinary #frontend #serverless
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-24 16:24 +0000
---

Most restaurants have a menu to help customers order food seamlessly. In this article, you'll build an app to store restaurant menus.

You'll be exploring the rich capabilities of Xata, a serverless database with built-in powerful search and analytics, and Cloudinary, a cloud-based image, and video management service.

## Prerequisites

A basic understanding of JavaScript and Next.js is needed to follow along with this article.

## Repository

You can find the complete code used in this article on [GitHub](https://github.com/folucode/restaurant-app-xata-cloudinary).

## Project Setup

[Node](https://nodejs.org/en/) has to be installed on your computer to set up the Next.js application. To install Node, go to the [Nodejs website](https://nodejs.org/en/) and follow the instructions to install the software compatible with your operating system. 

You can verify the Node.js installation by running the command below:


    node -v
    v16.10.0 //node version installed

To create the Next.js app, run the command below. It will automatically set up a boilerplate Next.js app.

`npx` stands for Node Package Execute. It executes any package from the npm registry without installing it.


    npx create-next-app@latest <app-name>
    # or
    yarn create next-app <app-name>

After the installation is complete, change the directory into the app you just created:


    cd <app-name>

Run `npm run dev` or `yarn dev` to start the development server on `http://localhost:3000`.


![nextjs](https://paper-attachments.dropboxusercontent.com/s_D6D02CF8B03C42412210DCFD6E1CDFF5C06A3DC4D7010024897EEBF1E682B292_1648074761781_nextjs-app.png)

## Setting up Xata database

Create a new database on your Xata dashboard called `restaurant_menu`

![xata dashboard](https://paper-attachments.dropboxusercontent.com/s_071945D63A60EFEAA1B90D16D3A83AFB6BF893E1CDBF0B404AE6B378059E64D7_1668551815459_Screenshot+2022-11-15+at+23.34.25.png)


Next, create a table and name it `meals`. Your table schema should look like this:

![xata dashboard](https://paper-attachments.dropboxusercontent.com/s_071945D63A60EFEAA1B90D16D3A83AFB6BF893E1CDBF0B404AE6B378059E64D7_1668552306964_Screenshot+2022-11-15+at+23.44.36.png)

## Setting up Xata instance

Run the command below to install the CLI globally:


    npm install @xata.io/cli -g

Next, run `xata auth login`, which will prompt you to Create a new API key in the browser or use an existing one; go with the first option.

![User-uploaded image: xata-create-new-api-key.png](https://www.dropbox.com/paper/ep/redirect/image?url=https%3A%2F%2Fpaper-attachments.dropboxusercontent.com%2Fs_767BD3F7E260EFDC7E9C209798C513C5E8831A1EBE6D22594749206A5E483F79_1668175038404_xata-create-new-api-key.png&hmac=g3BGJmRJoEPYOihMybnJS1VeTFPEMkDvqnhFC0H%2BzY0%3D)


Now, in the project directory, run `xata init`. You will be prompted with several options to set up Xata in your app. Choose the options below:

![xata initialization](https://paper-attachments.dropboxusercontent.com/s_071945D63A60EFEAA1B90D16D3A83AFB6BF893E1CDBF0B404AE6B378059E64D7_1668553043237_Screenshot+2022-11-15+at+23.50.51.png)

## Installing Cloudinary

[Cloudinary](https://cloudinary.com?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) provides a rich media management experience enabling users to upload, store, manage, manipulate, and deliver images and videos for websites and applications. 

Install the package with the code below:

    npm i @cloudinary/react
## Fetching data from the database

To fetch the data from the database, first replace the existing code in the `index.js` file with the code below:


    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    
    export default function Home({ data }) {
    
      return (
        <div className={styles.container}>
          <div>
            <h1 className={styles.header}>Restaurant Menu</h1>
          </div>
          <div className={styles.foodListContainer}>
            <FoodItem meals={data} />
          </div>
        </div>
      );
    }
    
    export async function getServerSideProps() {
      const xata = getXataClient();
      const data = await xata.db.meals.getAll();
      return { props: { data } };
    }
    

In the root of your project create a `components` folder. In the `components` folder, create a  `FoodItem.js` file and add the code below:


    import styles from '../styles/Home.module.css';
    
    export default function FoodItem({ meals }) {
      return (
        <div>
          {meals.map((meal) => (
            <div className={styles.foodList} key={meal.id}>
              <img src={meal.image_url} alt='image' width='100%' height='250' />
              <div className={styles.details}>
                <p>{meal.name}</p>
                <p>
                  <b>Price:</b> {meal.price}
                </p>
              </div>
            </div>
          ))}
        </div>
      );
    }
    

In the `Home.module.css` file, replace the contents with the code below:


    .container {
      background-color: white;
      padding: 1.5rem;
      height: 100vh;
    }
    .header {
      color: black;
      font-weight: 900;
      text-align: center;
      border: solid lightblue 10px;
      padding: 20px;
    }
    .foodListContainer {
      width: 50%;
      height: auto;
      float: left;
      margin-right: 20px;
    }
    .foodList {
      width: 45%;
      height: auto;
      padding: 1%;
      display: inline-block;
      background-color: beige;
      margin: 2px;
    }
    .details {
      margin-left: 60px;
      color: black;
      display: inline-block;
      margin-left: 10px;
      width: 70%;
    }
    .form {
      width: 400px;
      height: auto;
      padding: 1%;
      display: inline-block;
      background-color: #626161;
    }
    .formInput {
      width: 100%;
      padding: 12px 20px;
      margin: 8px 0;
      display: inline-block;
      border: 1px solid #ccc;
      border-radius: 4px;
      box-sizing: border-box;
    }
    .submitInput {
      width: 100%;
      background-color: #4caf50;
      color: white;
      padding: 14px 20px;
      margin: 8px 0;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
    .submitInput:hover {
      background-color: #45a049;
    }
    

What has happened so far is that you:

- Imported the `getXataClient` utility and initialized a new instance
- Queried the `me``als` table and fetched all records with `getServerSideProps`
- Passed the fetched data to the `FoodItem` component through props.
- Did basic CSS setup
## Inserting new records in the database

To insert new data into the Xata database you created. First, you'd need to create a form and capture all the form data input by the user and send it via an API call to be stored in the database. Remember that you'd need to upload the Image selected by the user to Cloudinary.

In the index.js file, add the following code:

    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    
    export default function Home({ data }) {
      return (
        <div className={styles.container}>
          ...
          <div className={styles.form}>
            <label htmlFor='name'>
              <b>Name:</b>
            </label>
            <input
              type='text'
              name='name'
              className={styles.formInput}
              onChange={(e) => setName(e.target.value)}
            />
            <label htmlFor='price'>
              <b>Price:</b>
            </label>
            <input
              type='text'
              name='price'
              className={styles.formInput}
              onChange={(e) => setPrice(e.target.value)}
            />
            <label htmlFor='image'>
              <b>Image:</b>
            </label>
            <input
              type='file'
              name='image'
              className={styles.formInput}
              onChange={(e) => setImageSrc(e.target.files[0])}
            />
            <button
              onClick={handleOnSubmit}
              type='submit'
              className={styles.submitInput}
            >
              Submit
            </button>
          </div>
        </div>
      );
    }
    export async function getServerSideProps() {
      const xata = getXataClient();
      const data = await xata.db.meals.getAll();
      return { props: { data } };
    }
    

This is just a basic HTML form. Now you’d need to capture the input data like so:


    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../components/SearchResult';
    
    export default function Home({ data }) {
      const [imageSrc, setImageSrc] = useState([]);
      const [name, setName] = useState();
      const [price, setPrice] = useState();
    
      return (
      ...
      )
    }
    export async function getServerSideProps() {
      const xata = getXataClient();
      const data = await xata.db.meals.getAll();
      return { props: { data } };
    }
    

Now, create the function to upload the image to Cloudinary and store the data in the database. 

Create a function called `handleOnSubmit` and add the following code to it:


    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../components/SearchResult';
    export default function Home({ data }) {
      ...
      async function handleOnSubmit(event) {
        event.preventDefault();
        let response;
    
        if (imageSrc) {
          const body = new FormData();
          body.append('upload_preset', 'bn1pyehj');
          body.append('file', imageSrc);
          response = await fetch(
            'https://api.cloudinary.com/v1_1/chukwutosin/image/upload',
            {
              method: 'POST',
              body,
            }
          ).then((r) => r.json());
        }
    
        fetch('/api/add-food-item', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            price,
            name,
            image_url: response.secure_url,
          }),
        }).then((r) => alert('record added successfully'));
      }
    
      return (
        ...
    }
    export async function getServerSideProps() {
      const xata = getXataClient();
      const data = await xata.db.meals.getAll();
      return { props: { data } };
    }
     

What you’re doing in this function is that you’re:

- Uploading the image, if it exists, to Cloudinary using your upload preset. 
- Storing the data in the database through the next.js API. 

You can find your upload preset in the Upload tab of our Cloudinary settings page by clicking on the gear icon in the top right corner of the dashboard page. 

![upload preset cloudinary](https://paper-attachments.dropboxusercontent.com/s_4B2ADD3FEB8D8E1F33E2260860C62FB74C629B51D38EF19C3CBB685E1CC815E6_1658093368939_upload+preset+cloudinary.png)


By scrolling down to the bottom of the page to the upload presets section, you’ll see your upload preset, or there will be an option to create one if you don't have any.

## Storing data in the database

Create a new file in the `api` folder and name it `add-food-item.js`. Paste the code below in the file:


    import { getXataClient } from '../../src/xata';
    const xata = getXataClient();
    
    const handler = async (req, res) => {
      const { name, price, image_url } = req.body;
      await xata.db.meals.create({
        price: parseFloat(price),
        name,
        image_url,
      });
      res.end();
    };
    
    export default handler;
    

You get the data sent to the API and save it to the Xata database.

## Adding Search functionality

To create a search functionality, create a new component file called `SearchResult.js` and pastre the code below:


    import styles from '../styles/Home.module.css';
    
    export default function SearchResult({ meals }) {
      return (
        <div>
          {meals.map((meal) => (
            <div className={styles.foodList} key={meal.record.id}>
              <img
                src={meal.record.image_url}
                alt='image'
                width='100%'
                height='250'
              />
              <div className={styles.details}>
                <p>{meal.record.name}</p>
                <p>
                  <b>Price:</b> {meal.record.price}
                </p>
              </div>
            </div>
          ))}
        </div>
      );
    }
    

This file is to show the search results.

Next,  create a new file in the `api` folder called `search.js`  and paste the code below into it:


    import { getXataClient } from '../../src/xata';
    const xata = getXataClient();
    
    const handler = async (req, res) => {
      const { searchTerm } = req.body;
      const results = await xata.search.all(searchTerm);
      res.send(results);
    };
    
    export default handler;
    

This is to query the database for the search term.

## Displaying the search results

Hold the search term and search data in `useState` like so:


    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../components/SearchResult';
    
    export default function Home({ data }) {
      ...
      const [searchTerm, setSearchTerm] = useState();
      const [searchData, setSearchData] = useState();
      ....
    

Create a function to perform the search operation:

    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../components/SearchResult';
    
    export default function Home({ data }) {
     ...
      async function handleSearch(event) {
        event.preventDefault();
    
        const result = await fetch('/api/search', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            searchTerm,
          }),
        }).then((r) => r.json());
    
        setSearchData(result);
      }
     ...
    

Display the search results:


    import FoodItem from '../components/FoodItem';
    import styles from '../styles/Home.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../components/SearchResult';
    export default function Home({ data }) {
      ..
      return (
        <div className={styles.container}>
          ....
            {searchData ? <SearchResult meals={searchData} /> : ''}
    
            {searchData ? '' : <FoodItem meals={data} />}
          ...
    

In displaying the search result, you first check to see that `searchData` has a value so that the `SearchResult` component and `FoodItem` don’t overlap.

## Running the Application

To see the app in action, run `npm run dev`, and visit the URL. You should see a screen like this and be able to add and search for meals.


![nextjs app in action](https://paper-attachments.dropboxusercontent.com/s_071945D63A60EFEAA1B90D16D3A83AFB6BF893E1CDBF0B404AE6B378059E64D7_1668557785043_Screenshot+2022-11-16+at+01.16.12.png)

## Conclusion

In this article, you created a restaurant menu app that helped you explore using Xata for seamless database storage and Cloudinary for easy image uploads.

## Resources
- [Xata documentation](https://xata.io/docs/quickstart/index?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- [Cloudinary documentation](https://cloudinary.com/documentation?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- [Next.js documentation](https://nextjs.org/docs/getting-started)