Learn how to create your own food ordering application and by extension any e-commerce app using modern development tools like Strapi, Gridsome & Snipcart. In this episode: Consuming products with Gridsome and GraphQL.
Simply copy and paste the following command line in your terminal to create your first Strapi project.
npx create-strapi-app
my-project
This article is part of a guest series by the great Ekene Eze. He’s leading the Developer Experience team at Flutterwave and wrote this blog post through the Write for the Community program.
This series will walk you through the processes of setting up your own food ordering application and by extension any e-commerce app using modern development tools like Strapi, Gridsome and Snipcart.
Table of Contents: 1. Part 1 - Generating a Strapi app and creating products 2. Part 2 - Setting up a Gridsome project 3. Part 3 - Consuming products with Gridsome and GraphQL 4. Part 4 - Creating single product views with Gridsome templates 5. Part 5 - Implementing cart and checkout with Snipcart 6. Part 6 - Deploying the apps
In the last part, we walked through the process of setting up a Strapi application and creating products for our application. In this part, we will connect our Gridsome app with the Strapi app we created in the first part to fetch products and display for users. https://mealzers.netlify.app
The products we'll display in our Gridsome application will come from the Strapi products endpoint. Luckily for us, Gridsome makes it possible for us to fetch data from the Strapi products endpoint into the GraphQL data layer that manages the data for our Gridsome application.
To do this, we need to install Axios. Axios is a promise-based HTTP client for the browser that makes it possible for you to communicate between different services. Run the following command to install Axios:
npm i axios
With Axios installed, open the project's gridsome.server.js
file and update it with the snippet below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//gridsome.server.js
const axios = require("axios");
module.exports = function(api) {
// whitelist vuetify for webpack code here
};
api.loadSource(async (actions) => {
const { data } = await axios.get("http://localhost:1337/products");
const collection = actions.addCollection({
typeName: "Product",
});
for (const product of data) {
collection.addNode({
id: product.id,
title: product.title,
price: product.price,
rating: product.rating,
description: product.description,
image: product.image.formats.thumbnail.url,
instructions: product.instructions,
});
}
});
api.createPages(({ createPage }) => {
// Use the Pages API here: https://gridsome.org/docs/pages-api/
});
Here, we are making a basic API call to fetch a list of products from our Strapi products endpoint which I'm running locally at localhost:1337/products
. You can consider collection in the above snippet as an array where we'll push each individual product returned from the endpoint. Hence, collection.addNode
is the equivalence of the regular array.push
.
Each node contains all the details we want to get from a product, like the title, description, price etc. As a result, we can loop through the entire data returned and get the individual products as well as their respective details.
Having fetched these products from Strapi, we can display them for users to interact with on the frontend. To do that, first let's create a new src/components/Products.vue
component. Within this component, we'll create the template to organize the products using Vuetify's card components. Update it with the snippet below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// src/components/Products.vue
<template>
<div>
<h1
v-if="show"
align="center"
justify="center"
class="py-5 orange white--text"
>
{{ banner }}
</h1>
<v-container class="mb-3">
<v-row :align="align" no-gutters>
<v-col md="4" v-for="edge in $page.products.edges" :key="edge.node.id">
<g-link
:to="`/products/${edge.node.id}`"
style="text-decoration: none; color: inherit"
>
<v-card :loading="loading" class="ma-3" shaped>
<v-img
height="250"
:src="`http://localhost:1337${edge.node.image}`"
></v-img>
<v-card-title>{{ edge.node.title }}</v-card-title>
<v-card-text>
<v-row align="center" class="mx-0">
<v-rating
:value="edge.node.rating"
color="amber"
dense
half-increments
readonly
size="14"
></v-rating>
</v-row>
<div class="my-4 subtitle-1">$ {{ edge.node.price }}</div>
<div>
{{ `${edge.node.description.slice(0, 120)}...` }}
</div>
</v-card-text>
<v-card-actions>
<v-btn
rounded
outlined
color="orange"
text
>
Add to cart
</v-btn>
</v-card-actions>
</v-card>
</g-link>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
export default {
props: ["banner", "show"],
};
</script>
At the moment, all we've done is create a Vuetify template that will hold the product data coming from our GraphQL data layer. We have provisioned fields for product description, price, title, rating, and image. To show this component to the users, we'll have to render it in the Index page. So let's do minor updates to the index.vue
file.
In the script section, import the Products component like so:
1
2
//index.vue
import Products from "@/components/Products.vue";
In the template section, just after the
1
2
//index.vue
<Products show="false" banner="Best Sellers" />
Finally to get all the products data we need to render here from our data layer, let's define a GraphQL query on our Product type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// src/pages/index.vue
<page-query>
query{
products: allProduct{
edges{
node{
id,
title,
description,
rating,
price,
image
}
}
}
}
</page-query>
Here, we are querying for the product details from our GraphQL data layer to use in the template. In the query above, consider edges to be a regular array of products and node to be the individual product. Before we proceed, let's run this query in the GraphQL explorer and see what the result look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
{
"data": {
"products": {
"edges": [
{
"node": {
"id": "7",
"title": "Seafood",
"description": "Seafood is any form of sea life regarded as food by humans. Seafood prominently includes fish and shellfish. Shellfish include various species of molluscs, crustaceans, and echinoderms. Historically, sea mammals such as whales and dolphins have been consumed as food, though that happens to a lesser extent in modern times",
"price": 300,
"rating": 5,
"image": "/uploads/thumbnail_seafood_d3e69aad71.png"
}
},
{
"node": {
"id": "6",
"title": "Sandwich",
"description": "Spread mayo, butter or cream cheese all the way to the edges of each slice of bread to create a seal against wet sandwich fillings. Also, try packing high moisture ingredients, like tomatoes, pickles, and cucumbers, separately",
"price": 150,
"rating": 3,
"image": "/uploads/thumbnail_miscellaneous_e0812098cf.png"
}
},
{
"node": {
"id": "5",
"title": "Dessert",
"description": "Dessert is a course that concludes a meal. The course usually consists of sweet foods, such as confections dishes or fruit, and possibly a beverage such as dessert wine or liqueur, however in the United States it may include coffee, cheeses, nuts, or other savory items regarded as a separate course elsewhere",
"price": 100,
"rating": 4,
"image": "/uploads/thumbnail_dessert_4fdacb1066.png"
}
},
{
"node": {
"id": "4",
"title": "Lamb",
"description": "Lamb, hogget, and mutton are the meat of domestic sheep (species Ovis aries) at different ages. A sheep in its first year is called a lamb, and its meat is also called lamb",
"price": 250,
"rating": 3,
"image": "/uploads/thumbnail_lamb_8f452bb9a3.png"
}
},
{
"node": {
"id": "3",
"title": "Chicken wings",
"description": "Chicken is a type of domesticated fowl, a subspecies of the red junglefowl. It is one of the most common and widespread domestic animals",
"price": 400,
"rating": 3,
"image": "/uploads/thumbnail_chicken_c6742f8df3.png"
}
},
{
"node": {
"id": "2",
"title": "Homemade pasta",
"description": "Pasta is a staple food of traditional Italian cuisine, with the first reference dating to 1154 in Sicily. Humans have been eating beef since prehistoric times",
"price": 400,
"rating": 5,
"image": "/uploads/thumbnail_pasta_1e163eb9f7.png"
}
},
{
"node": {
"id": "1",
"title": "Grilled Beef",
"description": "Beef is the culinary name for meat from cattle, particularly skeletal muscle. Humans have been eating beef since prehistoric times. Beef is a source of high-quality protein and essential nutrients. Check below for preparation instructions",
"price": 300,
"rating": 4,
"image": "/uploads/thumbnail_beef_b95f539dc8.png"
}
}
]
}
}
}
At this point, if you save all the updates made to the index.vue
file, we should get an updated view on the browser at localhost:8080
:
At the moment, we've achieved our goal of rendering the products from our Strapi application on the Gridsome frontend app.
You might have noticed that when you click on any of the products, it opens a new route http://localhost:8080/products/{id}
which represents the single page route for that particular product. This is possible because we wrapped each individual product card in a <g-link>
tag which basically provides client-side routing capabilities to our Gridsome application:
1
2
3
4
5
6
<g-link
:to="`/products/${edge.node.id}`"
style="text-decoration: none; color: inherit"
>
// product card here
</g-link>
So quick recap. In this part, we've consumed the product endpoint we created with Strapi in the first part of this series. Gridsome's GraphQL data layer was instrumental in this process as we have demonstrated. It made it easy to access the data we need wherever we need. We also got our hands dirty with Vuetify, creating components and styling the product cards was equally just as fun. In the next part, we'll look at how we can display product details in a single product view page where customers can place orders.
Ekene is a Developer Experience Engineer and Technical Writer. He is currently working with the Developer Experience team at Netlify where they build tools, create content, open-source repos, and demos to teach and also help developers build a better web with Netlify's products and services.