Peter Ekene Eze Learn, Apply, Share

How to build an ecommerce site with Strapi, Vue.js, and Flutterwave

7 min read 2219

Building ecommerce applications offers you the opportunity to learn how to handle data from remote sources but more importantly, how to intentionally build web applications that communicate with different services.

Last weekend I picked up a new tool in my list of “to learn” tools. It is called Strapi. I enjoyed learning this tool as I got even more exposed to all the features it offers. As a frontend developer, I was excited by how fast I could build backend services that communicate effortlessly with my frontend application. This is why I decided to share what I’ve learned with you in hopes that you find it as helpful as I did (if you don’t already use this tool).

In this tutorial, you will learn how to build a mini ecommerce application using Vue.js, Strapi, and Flutterwave.

Tooling

To build out this tutorial, we are going to use these tools:

  • Vue.js — A lightweight progressive JavaScript framework for building user interfaces
  • Strapi — An open-sourced headless content management system completely built with JavaScript
  • Flutterwave — An online payment gateway that makes it possible for customers to pay for your products anywhere in the world

Prerequisites

Before we get started, here are a few things to note:

  • You should have Node.js installed on your computer
  • You should have an account on Flutterwave
  • You should be fairly familiar with Vue.js and JavaScript
  • You should install Postman — a Google Chrome tool for interacting with HTTP APIs

Getting started

Now that we’ve gotten the preliminaries out of the way, let’s talk about how we’ll build this product. First, we’ll use Strapi to create a /products endpoint. This endpoint will return a list of products we’ll be selling on our ecommerce store. Afterward, we’ll build a Vue.js client to render these products to customers, and finally, we’ll integrate Flutterwave to allow customers to pay for our products.

Create a Strapi project

The fastest and most recommended way to create Strapi projects is through the Strapi CLI. You can do this with yarn or npx as the case may be. Open a terminal window and run the command:

yarn create strapi-app VueStrap --quickstart
#OR
npx create-strapi-app VueStrap --quickstart

The --quickstart flag will set up the project with an SQLite database. If you intend to create a new project with a different database, you can omit the flag and the CLI will prompt you to choose your preferred database.

Start the project

If you created the project using the --quickstart flag, the project will start automatically on port 1337 on your browser, however, if you chose a custom database, you can run the following command to start the project:

We made a custom demo for .
No really. Click here to check it out.

yarn develop
#OR
npm run develop

Now if you navigate to localhost:1337/admin on your browser, you will see a form that allows you to create the first admin user. Fill it and click Ready to start:

strapi welcome

This will now launch the admin area where you can create the required products for our store:

strapi homepage

By design, we want to create a /products endpoint that we could call to return a list of products. To do this, create a  Product content type by clicking the big blue button and filling out the form like so:

strapi configuration

Clicking on Continue , will display a new section where you will be required to create fields for this endpoint. In our case, we’ll have just four fields for:

  • Title
  • Description
  • Price
  • Image

We’ll need two text fields for title and description, a number field for price and a media field for the product image. Here’s how we fill the fields:

strapi fields

After adding all the fields we specified, you can preview the product endpoint with all the added fields when you click on Finish:

click finish

And just like that, we’ve created a functional /products endpoint that we can use to create the products we’d like to show in our store. Let’s quickly save this collection and add a few products to it:

strapi screen with no products added

Click on the Products collection on the sidebar and click on Add New Product to start adding your products:

products page in strapi

Save this product and repeat the same process for as many products as you’d like to put up on your store. In the end, this is what my products look like:

product entries in strapi page

And that’s it! We have just created a completely functional /products endpoint that we can call from the client to retrieve a list of all the products we have created here.

Finally, let’s update the user roles and permissions section to properly manage access to this endpoint. At the moment if we try to access the /products endpoint, we’ll get a forbidden error:

update roles and permissions

This is happening because we’ve not authorized this public user to access this endpoint. To manage access, use the Roles and Permissions button on the sidebar, select public and check the boxes you will like to give public users access to:

selected roles and permissions

This way, every public user will now have access to the selected actions on our endpoint.

Let’s test this again using Postman. If you have the app installed, make a GET request to the localhost:1337/products endpoint:

GET request to the localhost endpoint

 

Voila! now we get the products.

Vue app setup

Now that we have the endpoint ready, let’s create the client to consume the data we’ve prepared on the server and display these products to customers. To do this, we’ll use the Vue CLI tool to scaffold a new Vue project. If you don’t already have Vue CLI installed, run the following command:

npm install -g @vue/cli-service-global
#OR
yarn global add @vue/cli-service-global

To create and start a new Vue project, run the following commands:

#create a new Vue project:
vue create vue-strapi

#navigate into the newly created app and start the dev server:
cd vue-strapi && npm run serve

The project will now be live at localhost:8080 on your browser.

Styling

As a personal preference, I use BootstrapVue when working with Vue.js applications. Install Bootstrap and BootstrapVue like so:

npm i bootstrap bootstrap-vue

Now you can make the Bootstrap package available in the project via the main entry file like this:

// main.js
import Vue from "vue";
import App from "./App.vue";
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue/dist/bootstrap-vue.css";
import BootstrapVue from "bootstrap-vue";
  Vue.config.productionTip = false;
  Vue.use(BootstrapVue);
  new Vue({
    render: (h) => h(App),
  }).$mount("#app");

Fetch the products

By default, our Vue project has a HelloWorld component. Delete that component and create a new Meals component to handle the API communications with our Strapi project:

# src/components/Meals.vue
<script>
export default {
  data() {
    return {
      meals: [],
    };
  },
  mounted() {
    fetch("http://localhost:1337/products")
      .then((res) => res.json())
      .then((data) => {
        this.meals = data;
      });
  }
};
</script>

Here, we are using Vue’s mounted() lifecycle method and the native browser API fetch to call the /products endpoint when our app loads. We have also defined a meals data property to receive the response coming from the API and populate the UI with it. Next, let’s use BootstrapVue’s UI components and Vue directives to render the product details in our meals component:

# src/components/Meals.vue
<template>
  <b-container>
    <div v-if="meals.length">
      <b-row>
        <div v-bind:key="meal.index" v-for="meal in meals">
          <b-col l="4">
            <b-card
              v-bind:img-src="`http://localhost:1337/uploads/beef_b538baa14d.png`"
              v-bind:title="meal.title"
              img-alt="Image"
              img-top
              tag="article"
              style="max-width: 20rem;"
              class="mb-2"
            >
              <b-card-text>{{ `${meal.description}` }}</b-card-text>
              <span>
                <strong>Price: ${{ `${meal.price}` }} </strong>
              </span>
              <b-button @click="placeOrder" variant="primary">Order meal</b-button>
            </b-card>
          </b-col>
        </div>
      </b-row>
    </div>
    <div v-else>
      <h5>Fetching meals . . .</h5>
    </div>
  </b-container>
</template>

With the meals data property, we iterate and render the individual products returned from the endpoint to show the product title, description, price, and image respectively. You might also notice that I’m using a single image for all the products, instead of rendering the associated images per product. I wanted to fix this before completing this tutorial but I figured it might make for a great challenge for you. Parse the image URL in the data response and dynamically display each product’s image yourself, feel free to share your code or send a PR to this repo when you’re done.

Update the app component

Now that we have our Meals component ready, let’s update the App.vue component to render the Meals component we’ve just defined:

// src/App.vue
<template>
  <div>
    <br />
    <Meals />
  </div>
</template>
<script>
import Meals from "./components/Meals";
export default {
  name: "App",
  components: {
    Meals,
  },
};
</script>
<style>
#styles here
</style>

When you save and check on the browser, you should now see the app working as expected like so:

menu of various meals

Add a navbar

Since we are building a mini ecommerce application, it would make sense to ensure that it looks as close to a real web app as possible. Thankfully, BootstrapVue makes this possible in just a few steps. Create a new Navbar.vue component in the components folder and update it with the snippet below:

#src/components/navbar.vue
<template>
  <div>
    <b-navbar toggleable="lg" type="dark" variant="info">
      <b-navbar-brand href="#">MealsHub</b-navbar-brand>
      <b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
      <b-collapse id="nav-collapse" is-nav>
        <b-navbar-nav class="ml-auto">
          <b-nav-form>
            <b-form-input
              size="sm"
              class="mr-sm-2"
              placeholder="Search"
            ></b-form-input>
            <b-button size="sm" class="my-2 my-sm-0" type="submit"
              >Search</b-button
            >
          </b-nav-form>
          <b-nav-item-dropdown right>
            <template v-slot:button-content>
              <em>User</em>
            </template>
            <b-dropdown-item href="#">Profile</b-dropdown-item>
            <b-dropdown-item href="#">Sign Out</b-dropdown-item>
          </b-nav-item-dropdown>
        </b-navbar-nav>
      </b-collapse>
    </b-navbar>
  </div>
</template>
<script></script>

This is a BootstrapVue navbar component that does absolutely nothing apart from making our ecommerce store look a bit more real. If you update the \App.vue component with this navbar and render it before the Meals component, our store will take on a new look similar to this:

menu of meals with completed nav bar

Implement payments with Flutterwave

Flutterwave offers the easiest way to make and accept payments from customers anywhere in the world. What’s more, the integration instructions are very straightforward and the barrier to entry is very low as you’ll see shortly.

At the prerequisites, I mentioned that you will need a Flutterwave account, if you don’t have one yet, create a free account here. When you do, log in to your dashboard and toggle your account to sandbox mode. Then navigate to Settings on the sidebar, select the API tab and copy your public key:

Flutterwave API key

That’s all you need to set up a personal Flutterwave account. Next, add Flutterwave to our Vue app. Open the Meals component and create an instance of Flutterwave’s inline checkout modal and append it to the DOM using Vue’s created() lifecycle method:

// src/components/Meals.vue
    <script>
    export default {
      data() {
        return {
          meals: [],
        };
      },
      mounted() {
        //Fetch products
      },
      created() {
        const script = document.createElement("script");
        script.src = "https://checkout.flutterwave.com/v3.js";
        document.getElementsByTagName("head")[0].appendChild(script);
      },
    };
    </script>

Next, we define the placeOrder() function that will activate the Flutterwave’s checkout modal when the order meal button is clicked. We will do this using Vue’s methods property like so:

// src/components/Meals.vue
<script>
export default {
  data() {
    return {
      meals: [],
    };
  },
  methods: {
    placeOrder() {
      window.FlutterwaveCheckout({
        public_key: "INSERT YOUR PUBLIC KEY",
        tx_ref: "new-sale"+ new Date(),
        amount: 29.99,
        currency: "USD",
        country: "NG",
        payment_options: "card",
        customer: {
          email: "ekene@gmail.com",
          phone_number: "+234702909304",
          name: "Ekene Eze",
        },
        callback: function(data) {
          console.log(data);
        },
        onclose: function() {},
        customizations: {
          title: "MealsHub",
          description: "Payment for selected meal",
          logo: "http://localhost:1337/uploads/beef_b538baa14d.png",
        },
      });
    },
  },
  mounted() {
    fetch("http://localhost:1337/products"){
          // Fetch products
    });
  },
  created() {
      // Install Flutterwave
  },
};
</script>

The FlutterwaveCheckout function makes it possible for you to define the payment parameters that your customers are providing. This includes the amount to be charged, the currency to charge the customer in, the payment options you want to enable, and so on. The function also accepts a customer and a customizations object to provide more customizations on the checkout modal and lets you store more information about the paying customer.

At this point, if you click the order meal button, the Flutterwave checkout modal will be loaded for you to complete payment for the order:

menu order strapi app

Conclusion

In this project, we’ve built a mini ecommerce application using Vue.js, Strapi, and Flutterwave. With Strapi we were able to quickly build a /products endpoint that returns a list of products and with some details. With Vue, we built the client that consumes the products we created with Strapi to allow users on our store view and interact with our products. Finally, we integrated Flutterwave to allow customers pay for these products from anywhere in the world. For more resources on these tools, visit the documentation for Flutterwave, Strapi, and Vue.js.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

Peter Ekene Eze Learn, Apply, Share

9 Replies to “How to build an ecommerce site with Strapi, Vue.js,…”

  1. Hey you missed a part about the Roles & Permissions, without updating them, the endpoint throws a forbidden error 😛

  2. Hi Ekene,
    Thank you so much for this tutorial.
    I’m having a problem in payment connecting to flutterwave. It says Invalid public key passed even if i input the correct public key.
    This is the error in my network:{“status”:”error”,”message”:”Invalid public key passed”,”data”:{“code”:”INV_PUBK”,”message”:”Invalid public key passed”}}

    I suspect it’s something related to https://ravemodal-dev.herokuapp.com/v3.js connecting to flutterwave. Any idea on how to solve this? Thanks in advance.

    Regards,
    Saf

  3. All this sounds good, but how about adding payment valuation on our side? Flutterwave knows nothing about product prices and it means all the orders will be needed to be controlled manually.
    Ideally we should after popup close check on our server side if the payment was successful and here the mist interesting part begins: how easy is to setup connection from it to flutterwave and validate order?

  4. I think Someone can change the Flutterwave requested price, from the js console. How do i ascertain that a user payed the intended price?

    Thanks

  5. Hi Bogdan, very nice points you raised there. However, this post is only but an introduction to these tools, not necessarily an in-depth exposition of their features.

    The demo used in the post is also just “a demo”. Flutterwave handles payment validation and verification in a lot of ways. I’m sure you’ll find more if you look at the docs here https://developer.flutterwave.com/v3.0/docs/transaction-verification.

    In this post, I used the Flutterwave inline method, you can read more about it here https://developer.flutterwave.com/v3.0/docs/flutterwave-inline. You can also integrate via APIs https://developer.flutterwave.com/v3.0/docs/card-payments-1 It gives you more flexibility on how you handle processes and validation. Maybe some time we can deal with it in detail

Leave a Reply