Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Create a restaurant menu in NextJS using Xata and Cloudinary

Most eating places have a menu to assist prospects order meals seamlessly. On this article, you will construct an app to retailer restaurant menus.

You will be exploring the wealthy capabilities of Xata, a serverless database with built-in highly effective search and analytics, and Cloudinary, a cloud-based picture, and video administration service.



Conditions

A primary understanding of JavaScript and Subsequent.js is required to observe together with this text.



Repository

You will discover the entire code used on this article on GitHub.



Undertaking Setup

Node must be put in in your pc to arrange the Subsequent.js utility. To put in Node, go to the Nodejs website and observe the directions to put in the software program appropriate together with your working system.

You possibly can confirm the Node.js set up by operating the command under:

node -v
v16.10.0 //node model put in
Enter fullscreen mode

Exit fullscreen mode

To create the Subsequent.js app, run the command under. It would mechanically arrange a boilerplate Subsequent.js app.

npx stands for Node Bundle Execute. It executes any package deal from the npm registry with out putting in it.

npx create-next-app@newest <app-name>
# or
yarn create next-app <app-name>
Enter fullscreen mode

Exit fullscreen mode

After the set up is full, change the listing into the app you simply created:

cd <app-name>
Enter fullscreen mode

Exit fullscreen mode

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



Organising Xata database

Create a brand new database in your Xata dashboard referred to as restaurant_menu

xata dashboard

Subsequent, create a desk and title it meals. Your desk schema ought to appear like this:

xata dashboard



Organising Xata occasion

Run the command under to put in the CLI globally:

npm set up @xata.io/cli -g
Enter fullscreen mode

Exit fullscreen mode

Subsequent, run xata auth login, which can immediate you to Create a brand new API key within the browser or use an present one; go together with the primary choice.

User-uploaded image: xata-create-new-api-key.png

Now, within the mission listing, run xata init. You’ll be prompted with a number of choices to arrange Xata in your app. Select the choices under:

xata initialization



Putting in Cloudinary

Cloudinary supplies a wealthy media administration expertise enabling customers to add, retailer, handle, manipulate, and ship photographs and movies for web sites and functions.

Set up the package deal with the code under:

npm i @cloudinary/react
Enter fullscreen mode

Exit fullscreen mode



Fetching information from the database

To fetch the information from the database, first change the present code within the index.js file with the code under:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';

    export default perform House({ information }) {

      return (
        <div className={types.container}>
          <div>
            <h1 className={types.header}>Restaurant Menu</h1>
          </div>
          <div className={types.foodListContainer}>
            <FoodItem meals={information} />
          </div>
        </div>
      );
    }

    export async perform getServerSideProps() {
      const xata = getXataClient();
      const information = await xata.db.meals.getAll();
      return { props: { information } };
    }
Enter fullscreen mode

Exit fullscreen mode

Within the root of your mission create a elements folder. Within the elements folder, create a FoodItem.js file and add the code under:

    import types from '../types/House.module.css';

    export default perform FoodItem({ meals }) {
      return (
        <div>
          {meals.map((meal) => (
            <div className={types.foodList} key={meal.id}>
              <img src={meal.image_url} alt='picture' width='100%' peak='250' />
              <div className={types.particulars}>
                <p>{meal.title}</p>
                <p>
                  <b>Value:</b> {meal.value}
                </p>
              </div>
            </div>
          ))}
        </div>
      );
    }
Enter fullscreen mode

Exit fullscreen mode

Within the House.module.css file, change the contents with the code under:

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

Exit fullscreen mode

What has occurred up to now is that you simply:

  • Imported the getXataClient utility and initialized a brand new occasion
  • Queried the meals desk and fetched all data with getServerSideProps
  • Handed the fetched information to the FoodItem element by means of props.
  • Did primary CSS setup
    ## Inserting new data within the database

To insert new information into the Xata database you created. First, you’d must create a type and seize all the shape information enter by the person and ship it by way of an API name to be saved within the database. Do not forget that you’d must add the Picture chosen by the person to Cloudinary.

Within the index.js file, add the next code:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';

    export default perform House({ information }) {
      return (
        <div className={types.container}>
          ...
          <div className={types.type}>
            <label htmlFor='title'>
              <b>Title:</b>
            </label>
            <enter
              kind='textual content'
              title='title'
              className={types.formInput}
              onChange={(e) => setName(e.goal.worth)}
            />
            <label htmlFor='value'>
              <b>Value:</b>
            </label>
            <enter
              kind='textual content'
              title='value'
              className={types.formInput}
              onChange={(e) => setPrice(e.goal.worth)}
            />
            <label htmlFor='picture'>
              <b>Picture:</b>
            </label>
            <enter
              kind='file'
              title='picture'
              className={types.formInput}
              onChange={(e) => setImageSrc(e.goal.information[0])}
            />
            <button
              onClick={handleOnSubmit}
              kind='submit'
              className={types.submitInput}
            >
              Submit
            </button>
          </div>
        </div>
      );
    }
    export async perform getServerSideProps() {
      const xata = getXataClient();
      const information = await xata.db.meals.getAll();
      return { props: { information } };
    }
Enter fullscreen mode

Exit fullscreen mode

That is only a primary HTML type. Now you’d must seize the enter information like so:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../elements/SearchResult';

    export default perform House({ information }) {
      const [imageSrc, setImageSrc] = useState([]);
      const [name, setName] = useState();
      const [price, setPrice] = useState();

      return (
      ...
      )
    }
    export async perform getServerSideProps() {
      const xata = getXataClient();
      const information = await xata.db.meals.getAll();
      return { props: { information } };
    }
Enter fullscreen mode

Exit fullscreen mode

Now, create the perform to add the picture to Cloudinary and retailer the information within the database.

Create a perform referred to as handleOnSubmit and add the next code to it:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../elements/SearchResult';
    export default perform House({ information }) {
      ...
      async perform handleOnSubmit(occasion) {
        occasion.preventDefault();
        let response;

        if (imageSrc) {
          const physique = new FormData();
          physique.append('upload_preset', 'bn1pyehj');
          physique.append('file', imageSrc);
          response = await fetch(
            'https://api.cloudinary.com/v1_1/chukwutosin/picture/add',
            {
              technique: 'POST',
              physique,
            }
          ).then((r) => r.json());
        }

        fetch('/api/add-food-item', {
          technique: 'POST',
          headers: {
            'Content material-Sort': 'utility/json',
          },
          physique: JSON.stringify({
            value,
            title,
            image_url: response.secure_url,
          }),
        }).then((r) => alert('report added efficiently'));
      }

      return (
        ...
    }
    export async perform getServerSideProps() {
      const xata = getXataClient();
      const information = await xata.db.meals.getAll();
      return { props: { information } };
    }
Enter fullscreen mode

Exit fullscreen mode

What you’re doing on this perform is that you simply’re:

  • Importing the picture, if it exists, to Cloudinary utilizing your add preset.
  • Storing the information within the database by means of the following.js API.

You will discover your add preset within the Add tab of our Cloudinary settings web page by clicking on the gear icon within the high proper nook of the dashboard web page.

upload preset cloudinary

By scrolling right down to the underside of the web page to the add presets part, you’ll see your add preset, or there can be an choice to create one if you have no.



Storing information within the database

Create a brand new file within the api folder and title it add-food-item.js. Paste the code under within the file:

    import { getXataClient } from '../../src/xata';
    const xata = getXataClient();

    const handler = async (req, res) => {
      const { title, value, image_url } = req.physique;
      await xata.db.meals.create({
        value: parseFloat(value),
        title,
        image_url,
      });
      res.finish();
    };

    export default handler;
Enter fullscreen mode

Exit fullscreen mode

You get the information despatched to the API and reserve it to the Xata database.



Including Search performance

To create a search performance, create a brand new element file referred to as SearchResult.js and pastre the code under:

    import types from '../types/House.module.css';

    export default perform SearchResult({ meals }) {
      return (
        <div>
          {meals.map((meal) => (
            <div className={types.foodList} key={meal.report.id}>
              <img
                src={meal.report.image_url}
                alt='picture'
                width='100%'
                peak='250'
              />
              <div className={types.particulars}>
                <p>{meal.report.title}</p>
                <p>
                  <b>Value:</b> {meal.report.value}
                </p>
              </div>
            </div>
          ))}
        </div>
      );
    }
Enter fullscreen mode

Exit fullscreen mode

This file is to indicate the search outcomes.

Subsequent, create a brand new file within the api folder referred to as search.js and paste the code under into it:

    import { getXataClient } from '../../src/xata';
    const xata = getXataClient();

    const handler = async (req, res) => {
      const { searchTerm } = req.physique;
      const outcomes = await xata.search.all(searchTerm);
      res.ship(outcomes);
    };

    export default handler;
Enter fullscreen mode

Exit fullscreen mode

That is to question the database for the search time period.



Displaying the search outcomes

Maintain the search time period and search information in useState like so:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../elements/SearchResult';

    export default perform House({ information }) {
      ...
      const [searchTerm, setSearchTerm] = useState();
      const [searchData, setSearchData] = useState();
      ....
Enter fullscreen mode

Exit fullscreen mode

Create a perform to carry out the search operation:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../elements/SearchResult';

    export default perform House({ information }) {
     ...
      async perform handleSearch(occasion) {
        occasion.preventDefault();

        const outcome = await fetch('/api/search', {
          technique: 'POST',
          headers: {
            'Content material-Sort': 'utility/json',
          },
          physique: JSON.stringify({
            searchTerm,
          }),
        }).then((r) => r.json());

        setSearchData(outcome);
      }
     ...
Enter fullscreen mode

Exit fullscreen mode

Show the search outcomes:

    import FoodItem from '../elements/FoodItem';
    import types from '../types/House.module.css';
    import { getXataClient } from '../src/xata';
    import { useState } from 'react';
    import SearchResult from '../elements/SearchResult';
    export default perform House({ information }) {
      ..
      return (
        <div className={types.container}>
          ....
            {searchData ? <SearchResult meals={searchData} /> : ''}

            {searchData ? '' : <FoodItem meals={information} />}
          ...

Enter fullscreen mode

Exit fullscreen mode

In displaying the search outcome, you first examine to see that searchData has a worth in order that the SearchResult element and FoodItem don’t overlap.



Operating the Utility

To see the app in motion, run npm run dev, and go to the URL. You need to see a display screen like this and be capable to add and seek for meals.

nextjs app in action



Conclusion

On this article, you created a restaurant menu app that helped you discover utilizing Xata for seamless database storage and Cloudinary for straightforward picture uploads.



Sources

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?