Product
Resources
Ryan Belke
July 9, 2020
This tutorial is part of the « Cooking a Deliveroo clone with Next.js (React), GraphQL, Strapi and Stripe » tutorial series.
Table of contents
_Note: the source code is available on GitHub: https://github.com/strapi/community-content/edit/master/tutorials/code/next-react-hooks-strapi-food-delivery
Congratulations, you successfully displayed the list of restaurants!
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.Here is the final result:
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.
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.
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