This tutorial is part of the « E-commerce website with Strapi, Nuxt.js, GraphQL and Stripe
Note: The source code is available on Github
The installation script has just created an empty project. We will now start configuring it using the Content Types Builder, which allows you to create your content architecture using single or collection types. It is a core plugin of Strapi, only accessible when the application is in a development environment.
You will display a list of restaurants in your web app. The list is going to be managed through your Strapi API. At the end of this section, you should have a Homepage that looks like this:
A Strapi API includes, by default, the user
Content Type. Right now, you need restaurants, so your new Content Type is going to be, as you already guessed, restaurant
.
Restaurant
for the Display name, and click Continue.name
in the Name field.image
under the Name field, and check the Single media option then click Finish.Well done! You created your first Content Type. The next step is to add some restaurants to your database.
Let's create a restaurant.
Create as many restaurants as you would like to see in your app. If you're lacking some inspiration, you can check Deliveroo 🙈 . Having the items in database is great. Being able to request them from the Strapi API is even better.
When you were creating your restaurant
Content Type, Strapi created, behind the scenes, a set of files located in api/restaurant
. These files include the logic to expose a fully customizable CRUD API. The find
route is available at http://localhost:1337/api/restaurants. Try to visit this URL and will be surprised to be blocked by a 403 forbidden error. This is actually totally normal, new Strapi APIs are secured by design.
Don't worry, making this route accessible is actually super intuitive:
We have just added some new restaurants. We now have enough content to consume. But first, we need to make sure that the content is publicly accessible through the API:
Now go back to http://localhost:1337/api/restaurants. At this point, you should be able to see your list of restaurants. By default, the APIs generated with Strapi use REST conventions. In the next step, you will transform them into GraphQL.
We will use the GraphQL plugin in our Strapi project to fetch and mutate your content. To use the plugin, run the command below.
1 # Ctrl + C to close process
2 cd backend
3 npm run strapi install graphql
This plugin will add GraphQL functionality to your app. After the installation is complete:
npm run develop
to start the development server.[http://localhost:1337/graphql](http://localhost:1337/graphql)
to access GraphQL Playground.The GraphQL Playground has an inbuilt text editor for you to enter your GraphQL commands, a play button for you to run your code and a screen to display the return values, error, or success message. Try the following query in your GraphQL Playground:
1 query Restaurants {
2 restaurants {
3 data {
4 id
5 }
6 }
7 }
You should see the restaurants. If you did, you are ready to go onto the next step.
Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. Use it to fetch, cache, and modify application data, all while automatically updating your UI. Let's install the packages we need. Open a new terminal:
1 cd frontend
2 npm install @nuxtjs/apollo graphql-tag
Now that we have the dependencies we need, let's import the @nuxtjs/apollo
. Add the following module and configurations to your nuxt.config.js
1 // nuxt.config.js
2 const strapiBaseUri = process.env.API_URL || "http://localhost:1337";
3 export default {
4 // Modules: https://go.nuxtjs.dev/config-modules
5 modules: ['@nuxtjs/apollo'],
6
7 // Apollo: https://github.com/nuxt-community/apollo-module#usage
8 apollo: {
9 clientConfigs: {
10 default: {
11 httpEndpoint: `${strapiBaseUri}/graphql`,
12 }
13 }
14 }
15 }
It looks you are going to the right direction. In the next step, you will display these restaurants in your Nuxt.js app.
Open pages/index.vue
with your text editor, and copy/paste the following code
1 // pages/index.vue
2 <template>
3 <div class="uk-container uk-container-xsmall">
4 <h1 class="uk-heading-small">
5 <span class="uk-invisible">Restaurants</span>
6 <input
7 v-model="query"
8 class="uk-search-input"
9 type="search"
10 placeholder="Type to search"
11 />
12 </h1>
13
14 <div
15 v-for="restaurant in filteredList"
16 :key="restaurant.id"
17 class="
18 uk-card uk-card-default uk-grid-collapse uk-child-width-1-2 uk-margin
19 "
20 uk-grid
21 >
22 <figure class="uk-flex-last uk-card-media-right uk-cover-container">
23 <img
24 :src="getStrapiMedia(restaurant.attributes.image.data.attributes.url)"
25 :alt="restaurant.attributes.image.data.attributes.alternativeText"
26 uk-cover
27 />
28 </figure>
29 <div>
30 <div class="uk-card-body uk-card-small">
31 <h2 class="uk-card-title">{{ restaurant.attributes.name }}</h2>
32 <NuxtLink
33 class="uk-button uk-button-text"
34 :to="{ name: 'restaurants-id', params: { id: restaurant.id } }"
35 >
36 See dishes
37 </NuxtLink>
38 </div>
39 </div>
40 </div>
41 <div v-if="filteredList.length == 0" class="uk-heading-small">
42 <p>No restaurants found</p>
43 </div>
44 </div>
45 </template>
46
47 <script>
48 import { getStrapiMedia } from '@/utils/media'
49 import restaurantsQuery from '@/apollo/queries/restaurants'
50 export default {
51 data() {
52 return {
53 restaurants: [],
54 query: '',
55 }
56 },
57 apollo: {
58 restaurants: {
59 prefetch: true,
60 query: restaurantsQuery,
61 },
62 },
63 computed: {
64 filteredList() {
65 if (!this.restaurants?.data) return []
66 return this.restaurants?.data?.filter((restaurant) => {
67 return restaurant.attributes.name
68 .toLowerCase()
69 .includes(this.query.toLowerCase())
70 })
71 },
72 },
73 methods: {
74 getStrapiMedia,
75 },
76 }
77 </script>
We’re using UIkit a lightweight and modular front-end framework for developing fast and powerful web interfaces, it contains a set of layout components like Card
and Grid
that make it easy to style your website.
Let’s run the following command to install uikit:
1 npm install uikit
To import UIkit, create a new plugins/uikit.js
and copy/paste the following code.
1 // plugins/uikit.js
2 import Vue from 'vue'
3
4 import UIkit from 'uikit/dist/js/uikit-core'
5 import Icons from 'uikit/dist/js/uikit-icons'
6
7 UIkit.use(Icons)
8 UIkit.container = '#__nuxt'
9
10 Vue.prototype.$uikit = UIkit
Reference the new UIkit in your nuxt.config.js
like this.
1 // nuxt.config.js
2 export default {
3 // Global CSS: https://go.nuxtjs.dev/config-css
4 css: [
5 "uikit/dist/css/uikit.min.css",
6 "uikit/dist/css/uikit.css",
7 ],
8
9 // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
10 plugins: [
11 { src: '~/plugins/uikit.js', ssr: false }
12 ]
13 }
We can load GraphQL queries over .gql
files. This enable queries to be separated from logic.
Let’s create a new graphql
template. Create a new file apollo/queries/restaurants.gql
, and copy/paste the following code:
1 query Restaurants {
2 restaurants {
3 data {
4 id
5 attributes {
6 name
7 image {
8 data {
9 attributes {
10 url
11 alternativeText
12 }
13 }
14 }
15 }
16 }
17 }
18 }
Finally, let’s create a new folder utils
inside the frontend
directory, where we will put our commons helpers.
Then create a new empty file media.js
in the utils
directory, media.js
will provide us with a tiny helper to obtain images absolute URL.
Let’s add our getStrapiMedia
util to the utils/media.js
file:
1 // utils/media.js
2 export function getStrapiMedia(url) {
3 // Check if URL is a local path
4 if (url.startsWith("/")) {
5 // Prepend Strapi address
6 return `${process.env.strapiBaseUri}${url}`;
7 }
8 // Otherwise return full URL
9 return url;
10 }
And create a new environment variable in your nuxt.config.js
:
1 // nuxt.config.js
2 export default {
3 // ENV: https://nuxtjs.org/docs/configuration-glossary/configuration-env
4 env: {
5 strapiBaseUri,
6 },
7 }
This will append our Strapi Base URI to the image source. Great! After completing the homepage:
npm run dev
to start the development server.http://localhost:3000
to view your application.Well done! You can now see your restaurants! 🍔 In the next section, you will learn how to display the list of dishes.
Pierre created Strapi with Aurélien and Jim back in 2015. He's a strong believer in open-source, remote and people-first organizations. You can also find him regularly windsurfing or mountain-biking!