Strapi blog logo
  • Product

      Why Strapi?Content ArchitectureRoadmapIntegrationsTry live demo
      OverviewContent Types BuilderCustomizable APIMedia LibraryRoles & Permissions
      Discover Strapi Enterprise EditionDiscover our partners
  • Pricing

  • Solutions

      Static WebsitesMobile ApplicationsCorporate websitesEditorial SitesEcommerce
      Delivery HeroL'EquipeSociete GeneralePixelDustBanco International
      Discover all our user stories
  • Community

      CommunityWrite for the communityWall of LoveStrapi Conf 2021
      SlackGitHubYoutubeCommunity Forum
      Meet the Strapi Community StarsDiscover the Strapi Showcase
  • Resources

      BlogStartersNewsroomSupport
      Strapi AcademyTutorialsVideosWebinars
      The Guide to Headless CMS Strapi Community Forum
  • Docs

      InstallationConfigurationsDeploymentUpdate versionContent API
      Getting StartedContent ManagerContent-Types BuilderUsers, Roles & PermissionsPluginsSettings
      Developer DocumentationStrapi User Guide

Looking for our logo ?

Logo Brand download
Download Logo Pack
See more Strapi assets
Strapi blog logo
  • Product

    Product

    • Why Strapi?
    • Content Architecture
    • Roadmap
    • Integrations
    • Try live demo

    Features

    • Overview
    • Content Types Builder
    • Customizable API
    • Media Library
    • Roles & Permissions
    • Discover Strapi Enterprise Edition
    • Discover our partners
    Features cover

    Unlock the full potential of content management

    See all features
    Strapi Enterprise Edition

    Discover the advanced features included in Strapi Enterprise Edition.

    Get Started
  • Pricing
  • Solutions

    Solutions

    • Static Websites
    • Mobile Applications
    • Corporate websites
    • Editorial Sites
    • Ecommerce

    Stories

    • Delivery Hero
    • L'Equipe
    • Societe Generale
    • PixelDust
    • Banco International
    • Discover all our user stories
    Delivery Hero team

    Delivery Hero manages their partner portal with Strapi.

    Read their story
    turn 10 studios website

    How 1minus1 delivered a creative website for Turn10 Studios 25% faster with Strapi

    Discover their story
  • Community

    Community

    • Community
    • Write for the community
    • Wall of Love
    • Strapi Conf 2021

    Resources

    • Slack
    • GitHub
    • Youtube
    • Community Forum
    • Meet the Strapi Community Stars
    • Discover the Strapi Showcase
    Strapi Conf

    The first Strapi Global User Conference.

    Register now
    Write for the community

    Contribute on educational content for the community

    Discover the program
  • Resources

    Resources

    • Blog
    • Starters
    • Newsroom
    • Support

    Learning

    • Strapi Academy
    • Tutorials
    • Videos
    • Webinars
    • The Guide to Headless CMS
    • Strapi Community Forum
    Introducing Strapi Academy

    Everything you need to know to master Strapi.

    Go to the academy
    Strapi Repository on GitHub

    Get started with the Strapi repository

    Go to repository
  • Docs

    Developers

    • Installation
    • Configurations
    • Deployment
    • Update version
    • Content API

    User Guide

    • Getting Started
    • Content Manager
    • Content-Types Builder
    • Users, Roles & Permissions
    • Plugins
    • Settings
    • Developer Documentation
    • Strapi User Guide
    Install Strapi

    Install Strapi locally or wherever you need.

    Get Started
    Migration Guides Strapi

    Using a previous version of Strapi? Migrate to the latest.

    Read Guides
Get Started
Back to articles

Create a food ordering app with Strapi and Next.js 3/7

ordering-app
  • Share on facebook
  • Share on linkedin
  • Share on twitter
  • Share by email
Ryan Belke

Ryan Belke

July 9, 2020

Strapi Next.js tutorial

This tutorial is part of the « Cooking a Deliveroo clone with Next.js (React), GraphQL, Strapi and Stripe » tutorial series.

Table of contents

  • 🏗️ Setup (part 1)
  • 🏠 Restaurants (part 2)
  • 🍔 Dishes (part 3) - current
  • 🔐 Authentication (part 4)
  • 🛒 Shopping Cart (part 5)
  • 💵 Order and Checkout (part 6)
  • 🚀 Bonus: Deploy (part 7)

_Note: the source code is available on GitHub: https://github.com/strapi/community-content/edit/master/tutorials/code/next-react-hooks-strapi-food-delivery

🍔 Dishes list

Congratulations, you successfully displayed the list of restaurants!

Define Content Type

Every restaurant sells dishes, which also must be stored in the database. Now we need a new Content Type, named dish. Create a new Content Type the same way with the following attributes:

  • name with type Text.
  • description with type Text (Long Text).
  • image with type Media (Single media).
  • price with type Number (Decimal).
  • restaurant with type Relation: this one is a bit more specific. Our purpose here is to tell Strapi that every dish can be related to a restaurant. To do so, create a one-to-many relation, as below.

Strapi relation

Here is the final result:

Dishes fields

Add some entries

Then, add some dishes from the Content Manager: http://localhost:1337/admin/plugins/content-manager/dish. Make sure they all have an image and are linked to a restaurant.

Display dishes

Dishes list

We will use a new route for /restaurants that will take in the ID of restaurant and return the list of dishes for that restaurant:

$ cd ..
$ cd ..
$ cd pages
$ touch restaurants.js

Path: /frontend/pages/restaurants.js

/* /pages/restaurants.js */
import { useQuery } from "@apollo/react-hooks";
import { useRouter } from "next/router";
import { gql } from "apollo-boost";

import {
  Button,
  Card,
  CardBody,
  CardImg,
  CardText,
  CardTitle,
  Col,
  Row,
} from "reactstrap";

const GET_RESTAURANT_DISHES = gql`
  query($id: ID!) {
    restaurant(id: $id) {
      id
      name
      dishes {
        id
        name
        description
        price
        image {
          url
        }
      }
    }
  }
`;

function Restaurants(props) {
  const router = useRouter();
  const { loading, error, data } = useQuery(GET_RESTAURANT_DISHES, {
    variables: { id: router.query.id },
  });

  if (error) return "Error Loading Dishes";
  if (loading) return <h1>Loading ...</h1>;
  if (data.restaurant) {
    const { restaurant } = data;
    return (
      <>
        <h1>{restaurant.name}</h1>
        <Row>
          {restaurant.dishes.map((res) => (
            <Col xs="6" sm="4" style={{ padding: 0 }} key={res.id}>
              <Card style={{ margin: "0 10px" }}>
                <CardImg
                  top={true}
                  style={{ height: 250 }}
                  src={`${process.env.NEXT_PUBLIC_API_URL}${res.image.url}`}
                />
                <CardBody>
                  <CardTitle>{res.name}</CardTitle>
                  <CardText>{res.description}</CardText>
                </CardBody>
                <div className="card-footer">
                  <Button outline color="primary">
                    + Add To Cart
                  </Button>

                  <style jsx>
                    {`
                      a {
                        color: white;
                      }
                      a:link {
                        text-decoration: none;
                        color: white;
                      }
                      .container-fluid {
                        margin-bottom: 30px;
                      }
                      .btn-outline-primary {
                        color: #007bff !important;
                      }
                      a:hover {
                        color: white !important;
                      }
                    `}
                  </style>
                </div>
              </Card>
            </Col>
          ))}
        </Row>
      </>
    );
  }
  return <h1>Add Dishes</h1>;
}
export default Restaurants;

Now you should see your list of dishes associated with that restaurant if you navigate via the browser.

We will add the cart in the upcoming sections, hang tight!

You will notice that if you navigate to the restaurant route and hit refresh, you will get a 404 page. This is because when you click the Link component the client is handling the routing, but on refresh the route is not found by the server.

404

To handle this we can setup an express server coupled with Next to render the route properly.

yarn add express

Next create a file server.js inside the root of your project directory.

cd ..
touch server.js

Path: /frontend/server.js

/* server.js */

const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
	.then(() => {
		const server = express()

		server.get('/restaurants/:id', (req, res) => {
			const actualPage = '/restaurants'
			const queryParams = { id: req.params.id }
			console.dir("req.params.id = " + JSON.stringify(req.params.id))
			app.render(req, res, actualPage, queryParams)
		})

		server.get('*', (req, res) => {
			return handle(req, res)
		})

		server.listen(3000, (err) => {
			if (err) throw err
			console.log('> Ready on http://localhost:3000')
		})
	})
	.catch((ex) => {
		console.error(ex.stack)
		process.exit(1)
	})

To use the custom express server edit your packages.json file and replace the script section with:

  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }

Restart the dev server, now with a refresh you should see the appropriate dishes as expected on a page refresh.

🔐 In the next section, you will learn how to authenticate users in your app (register, logout & login): https://strapi.io/blog/nextjs-react-hooks-strapi-auth-4

  • Share on facebook
  • Share on linkedin
  • Share on twitter
  • Share by email

Unleash content.

Starters
Get Started

Strapi is the leading open-source Headless CMS. Strapi gives developers the freedom to use their favorite tools and frameworks while allowing editors to easily manage their content and distribute it anywhere.

Product

  • Why Strapi?
  • Content Architecture
  • Features
  • Enterprise Edition
  • Partner Program
  • Roadmap
  • Support
  • Try live demo
  • Changelog

Resources

  • How to get started
  • Meet the community
  • Tutorials
  • API documentation
  • GitHub repository
  • Starters
  • Strapi vs Wordpress
  • The Guide to headless CMS

Integrations

  • All integrations
  • React CMS
  • Next.js CMS
  • Gatsby CMS
  • Vue.js CMS
  • Nuxt.js CMS
  • Gridsome CMS
  • Flutter CMS
  • Hugo CMS
  • Typescript CMS

Company

  • About us
  • Blog
  • Careers
  • Contact
  • Newsroom
  • © 2021, Strapi
  • LicenseTermsPrivacy