A Beginner's Guide to Strapi Plugin Development

A Beginner's Guide to Strapi Plugin Development

Learn what a Strapi plugin is and how the Strapi scheduler plugin in an online book store application using Vue.js and Strapi.

Author: Mary Okosun

Strapi is an open-source and headless content management system ( CMS ) that gives developers the freedom to use their favorite tools and frameworks during development. Strapi has an official web plugin marketplace, The Strapi Market, which contains more than sixty (60) plugins to choose from.

What is a Strapi Plugin and Why Create a Plugin?

Plugins, also referred to as add-ons, are software available to give extra functionalities to our application. They have been known to help developers customize their applications in regards to styles and easy accessibility.

Strapi plugins were developed to power up your Strapi app with industry-leading software and also easily add custom features. They can be installed from the Marketplace or as npm packages. You can either create your plugins or extend the existing ones.

Goal

This tutorial aims to give an approach to what a Strapi plugin is, and how to get started with Strapi plugin development as a beginner. You will learn how to create an online book store application using Vue.js and Strapi and how the Strapi scheduler plugin can be used to schedule books being added and also removed from our application.

At the end of this tutorial, you should know how to get started with Strapi plugin development and how to install and use plugins from the Strapi Market to power up your application.

Prerequisites

To build this project, you need to:

  1. Download and install Node.js (version 14 is recommended by Strapi).
  2. Have npm (version 6 only) or yarn to run the CLI installation scripts.
  3. Have a basic knowledge of JavaScript and Vue.js.

Building the Strapi Backend APIs

In this phase, we will build the Strapi backend APIs to kickstart our project.

Step 1: Scaffolding a Strapi Project

We will be running our Strapi project locally. Although we have multiple ways to start a new Strapi project like the starter CLI, the fastest way to do this is to install it via the Strapi Command Line Interface(CLI).

    npx create-strapi-app book-store-backend --quickstart
    #OR
    yarn create-strapi-app book-store-backend --quickstart

The command above will scaffold a new Strapi project in the directory you specified. It should automatically open localhost:1337/admin on your browser. Else, you can run the following command on your terminal to start the admin panel on your browser.

    npm run develop
    # OR
    yarn run develop

A new tab would be opened to register your new admin.

Strapi Admin Panel

You can fill out the form and click on the button to submit. Afterward, you will be directed to the admin dashboard.

Strapi Admin Dashboard

Step 2: Build the Bookstore Collection

We will create a new collection type that will store the details of each book to be added. We would need a collection type called book. This collection type would have details like name, author, description, and image.

We would follow these steps to create our collection type:

  1. On the left-hand side of the panel, click on the Collection-Type Builder and then on Create new collection type. Fill in Book as the display name. Click on continue to create a new collection.

  2. This prompts a new modal where you can select the fields for your collection type. Select Text and fill in Name at the Text field. Click on Add another field and select Text for the Description and Author fields. Select Media for the image field.

  3. After adding all the required fields, click on the Finish button and then Save the collection. Strapi Collection Types

Step 3: Populate the Bookstore Content

We will create content for the collection type we just created. We need to follow these steps to populate our collection:

  1. On the left-hand side of the panel, click on the Content Manager and then on the Book collection.

  2. This prompts a modal where you can click on Create new entry and fill in the content for the various fields. You can save these contents and publish them afterward. Strapi Create an entry layout

Step 4: Testing Your API

For this tutorial, we need to make two routes accessible to:

  • Get all published articles, and
  • Get a published article by ID.

After creating the book collection successfully, you need to allow public access because if we try to access the API URL with our public client, it would return a 403 forbidden error.

Postman GET endpoint(403)

We need to enable permissions and roles in the admin panel by following these steps:

  1. On the sidebar menu, click on Settings and then on Roles. In the new prompt that shows up, click on Public

  2. A card would show beneath, you can click on the Book permission and tick the find and findOne boxes. Save the roles afterward.

  3. You can test out your API as retrieving all books is now accessible at https://localhost/api/books and getting a published article by ID is accessible at https://localhost/api/books/:id Postman GET endpoint(200)

Getting Started with Scheduler Plugin

The Scheduler is a Strapi plugin that allows Strapi CMS users the ability to publish and depublish any content type in the future. It gives you the freedom to schedule a publish or unpublish date for your contents through some basic configurations to your backend files.

Installation

This plugin can be installed via the terminal:

    npm install @webbio/strapi-plugin-scheduler
    #OR
    yarn add @webbio/strapi-plugin-scheduler

Configuration

After installing, you need to create a new plugin.js file in your config folder. You need to include the following code snippets in your ./config/plugin.js

    module.exports = ({ env }) => ({
       scheduler: {
        enabled: true,
        config: {
          model: "scheduler",
        },
      },
      });

Create a cronTasks variable to require the Scheduler plugin in your ./config/server.js so the server.js file should contain the following snippets.

    const cronTasks = require("@webbio/strapi-plugin-scheduler/cron-task");

    module.exports = ({ env }) => ({
      host: env('HOST', '0.0.0.0'),
      port: env.int('PORT', 1337),
      cron: {
        enabled: true,
        tasks: cronTasks,
      },
    });

You can stop your server and run the application again using npm run develop. You will see some information added to the right sidebar. This new section allows you to choose a date and time to publish or unpublish your article.

Strapi Content Manager

Building the Bookstore Frontend App

Now that we have built our backend services and have the scheduler plugin configured, we will move on to creating our frontend to consume our APIs with Vue.js.

According to the documentation, Vue.js is a JavaScript framework for building user interfaces. It builds on top of standard HTML, CSS, and JavaScript, and provides a declarative and component-based programming model that helps you efficiently develop user interfaces, be it simple or complex.

To create a new Vue.js project, follow these steps to get started.

  1. Navigate to a directory and Install the Vue.js package using the command:

     npm install -g @vue/cli
     # OR
     yarn global add @vue/cli
    
  2. Create a new project using the command:

     vue create book-store-frontend
    
  3. You will be prompted to pick a preset. We would select "Manually select features" to pick the features we need. We would select Vuex, Router, and Lint/Formatter. Vuex is a state management library for Vue applications, Router allows to change the URL without reloading the page, and Lint/Formatter properly formats the codes. After successfully creating your project, we would navigate to the folder directory and run our application.

     cd book-store-frontend
     npm run serve
     #OR
     yarn run serve
    

    The URL localhost:8080 should open your Vue.js application in your browser.

Vue layout

Dependency Installation

We need to install some dependencies such as axios. Axios is the package dependency that will be used to make the call to the Strapi backend APIs.

    npm install axios

Firstly, paste the following code snippets in the ./store/index.js file:

    import { createStore } from "vuex";
    import axios from "axios";
    export default createStore({
      state: {
        bookStore: [],
      },
      getters: {
        getBookStore: (state) => state.bookStore,
      },
      mutations: {
        setAllBooks: (state, payload) => (state.bookStore = payload),
      },
      actions: {
        getAllBooks: ({ commit }) => {
          try {
            axios.get(`http://localhost:1337/api/books?populate=*`)
              .then((res) => {
              commit("setAllBooks", res.data.data);
            });
          } catch (e) {
            console.log("error", e);
          }
        },
      },
      modules: {},
    });

Afterward, in the views folder, delete the AboutView.vue and HomeView.vue files and create a BookView.vue and BookDetails.vue files.

In the BookView.vue file, paste the following code;

    <template>
      <div class="row">
        <div class="cols" v-for="book in getBookStore" :key="book.key">
          <router-link :to="{ name: 'BookDetails', params: { id: book.id } }">
            <img
              :src="`http://localhost:1337${book.attributes.Image.data.attributes.url}`"
            />
            <h3>{{ book.attributes.Name }}</h3>
            <h5>
              By :
              {{ book.attributes.Author }}
            </h5>
            <h5>
              Published at: <br />
              {{ book.attributes.publishedAt }}
            </h5>
          </router-link>
        </div>
      </div>
    </template>
    <script>
    import { mapGetters, mapActions } from "vuex";
    export default {
      name: "BookStore",
      data() {
        return {
          bookStore: [],
        };
      },
      mounted() {
        this.getAllBooks();
      },
      computed: {
        ...mapGetters(["getBookStore"]),
      },
      methods: {
        ...mapActions(["getAllBooks"]),
      },
    };
    </script>
    <style scoped>
    .row {
      margin: 0px 10px;
      display: flex;
      text-align: justify;
    }
    .cols {
      margin: 10px;
      width: 15vw;
      box-shadow: 4px 4px 4px #ae9f9f;
    }
    img {
      width: 100%;
      height: 150px;
    }
    a {
      text-decoration: none;
      color: #201c1c;
    }
    p {
      font-size: 15px;
      text-align: left;
    }
    h3,
    h5 {
      margin: 10px 10px;
    }
    </style>

In the BookDetails.vue file, paste the following code snippets;

    <template>
      <div class="books">
        <div class="card">
          <div class="card-desc">
            <img :src="`http://localhost:1337${image}`" />
            <div>
              <h3>{{ bookById.Name }}</h3>
              <h5>By: {{ bookById.Author }}</h5>
              <h5>Published at: {{ bookById.publishedAt }}</h5>
            </div>
            <p>{{ bookById.Description }}</p>
          </div>
        </div>
      </div>
    </template>
    <script>
    import axios from "axios";
    export default {
      name: "BookDetails",
      data() {
        return {
          image: [],
          bookById: [],
        };
      },
      async mounted() {
        const id = this.$route.params.id;
        await axios
          .get(`http://localhost:1337/api/books/${id}?populate=*`)
          .then((response) => {
            this.bookById = response.data.data.attributes;
            this.image = this.bookById.Image.data.attributes.url;
          });
      },
    };
    </script>
    <style scoped>
    .card {
      max-width: 80vw;
      margin: auto;
    }
    .card-title {
      display: flex;
      justify-content: space-around;
    }
    img {
      width: 20vw;
      height: 20vw;
    }
    h3 {
      font-size: 20px;
      color: #000;
    }
    h5 {
      font-size: 15px;
      font-style: italic;
      color: #716c69;
      margin: 5px !important;
    }
    p {
      font-size: 19px;
      color: #000;
      width: 50vw;
      padding-top: 20px;
      margin: auto;
      text-align: justify;
    }
    button {
      padding: 15px;
      background: blue;
      border: none;
      color: #fff;
      width: 100%;
    }
    .card-desc {
      margin: 0px 50px;
    }
    .title,
    .author {
      padding: 5px;
    }
    </style>

In the /router/index.js file, the code snippets to properly route our application should be similar to the snippets below:

    import { createRouter, createWebHistory } from "vue-router";
    const routes = [
      {
        path: "/",
        name: "BookStore",
        component: () => import("../views/BookView.vue"),
      },
      {
        path: "/book/:id",
        name: "BookDetails",
        component: () => import("../views/BookDetails.vue"),
      },
    ];
    const router = createRouter({
      mode: "history",
      history: createWebHistory(process.env.BASE_URL),
      routes,
    });
    export default router;

Testing the Application

Now refresh your browser, and you should see something similar to this page:

Frontend Implementation

When you click on the individual book card, the get published article by ID API would be called and a response would be returned for the book id.

Get book by ID frontend Implementation

Now, we have a working frontend and backend implementation. In the following steps, we would be scheduling our books to be published and de-published at a specific time in the future.

Note that you cannot schedule an already published content to be published, instead schedule it to be de-published. So we would create a new content draft to be published and schedule a date to de-publish one of the published contents we have in our application.

Set a Publish Time

To schedule a publish time, follow the same process to create new content for your collection. After saving the content, choose a publish date and time. Afterward, click on the Set publish time button. You would get a confirmation notification that Post has been scheduled.

Set an Unpublish Time

To schedule an unpublish time, click on Content Type and then Book. Select the entry you would want to de-publish and choose the date and time. Afterward, click on the Set depublish time button. You would get a confirmation notification that Post has been scheduled

Conclusion

In this tutorial, we learned what a Strapi plugin is, why you should use a plugin, and how to create an online book store application with Vue.js as the frontend framework and Strapi as the Backend. We demonstrated how the scheduler plugin can be used to schedule books being published and de-published from our application.

You can download the source code for the frontend and backend implementation from Github.