David Lengweiler

Programming Tutorials and Guides

tutorial web application - 1 February 2021

Strapi and Nuxt Webapplication

Frontend: NuxtJS + Backend: Strapi

Thumbnail

TLDR: We are building a minimal example using my preferred stack. For this we are going to have a look at Strapi as a backend and connect it to a specialized Vue.js frontend called NuxtJS.

Motivation

On my quest to find the perfect web development stack, if tested a lot of different frontend and backend frameworks and libraries over the years. Some tend to focus on more complex applications and tend to require a lot of unnecessary work when building medium-sized applications.

Frontend - Vue.js

Since I started working with web applications it always liked working with Javascript frontend frameworks, in particular Vue.js. In contrast to for example AngularJS, which places Javascript, HTML and CSS in separate files for each component, Vue.js places everything in the same file. This so-called Single File Component is most of the time perfect for my use-case and the size of my applications.

Example Single File Component

<template>
  <div class="example">
    <h1>Welcome to Vue.js.</h1>
  </div>
</template>

<script>
export default {
  name: "Example"
}
</script>

<style scoped>
  .example-style {
    background-color: blue;
  }
</style>

While vanilla Vue.js is pretty powerful already, JavaScript frameworks tend to be quite slow on initial load. This is the case, because they focus mainly on applications which handle dynamic content. To speed the initial loading up I use SSR (server side rendering), where the website is pre-rendered on the server and the result then send to the client. To set this up yourself can be extremely time-consuming. Luckily, there exists a rather comfortable framework in NuxtJS. NuxtJS combines the power of Vue.js, with SSR and comes with additional functionality, which can be use if needed.

Backend - Strapi

While my frontend choice was already set early on, I tried a lot of different backend solutions over the year and never was quite satisfied. This changed after I discovered Strapi. I like the simplicity of it and how easy it is to spin up and configure sources. It only comes with basic functionality to define different endpoints, and different self-defined types which can be retrieved through them. Its editor is straight forward and comes with all needed functionality to start writing content right away. Additionally, it has the ability to extend Strapi with additional plugins, which for example can exchange the default REST endpoints with GraphQL endpoints.

Strapi BackendStrapi Backend

Setup Backend - Strapi

Let us now start with our backend configuration. For that we are first going to get Strapi installed and running locally. This is extremely easy, and you only need to enter following command into your terminal:

npx create-strapi-app [frontend-name] --quickstart

[frontend-name] needs to be replaced with your name for the backend. I tend to place the front- and backend in the same folder, so I often choose to call this backend.

For this command to work you need to have npm on your system. If you do not have it already, you can get it by installing NodeJS, which also installs npm

If you stop the development server, you can simply restart it by executing following command from inside the project:

npm run develop

After successfully installing Strapi, you can access it through http://localhost:1337/admin, where you are asked to enter your first user.

Now, let us create our first content-type. For this navigate to Content-Types Builder and click + Create new single type. The first prompt will ask your for the type name, after that you can define different fields, which all entries of this type provide. For this tutorial I generated a content-type called Example with three different field types. Do not forget to save your newly generated content-type, or you will lose the configuration of your new content-type.

Single Content-Type

By visiting our new content-type in the sidebar we now can insert the information for our Examples.

Single content-types allow only a single "block" of information, if you want the ability to generate multiple similar types you would need to select + Create Collection-Type in the Content-Type Builder.

In the last step, we now need to enable our previously generated content-type to be queried when not authenticated. For this we navigate to Settings and select Roles, when opening APPLICATION, we can check find for your Examples content-type. Here, we also see the appropriate route to access the content-type:

Permissions Content-Type

If we save and published our previously defined Examples content-type correctly, we should get a similar output to following, when accessing http://localhost:1337/examples:

{
  "id":1,
  "text":"This is some text to show you how comfortable **Strapi** can be!",
  "isVisible":true,
  "Budget":32.69,
  "published_at":"2021-01-31T00:26:34.594Z",
  "created_at":"2021-01-31T00:12:40.766Z",
  "updated_at":"2021-01-31T00:26:34.611Z"
}

This is everything, which needs to be done to configure the backend, let us now have a look at how we use our newly generated endpoint in our frontend.

Setup Frontend - Vue.js (NuxtJS)

Create NuxtJS application

For our frontend we first have to create a new NuxtJS application. To do this, you can enter following commands in the console:

npx create-nuxt-app [backend-name]

Here, you again need to replace [backend-name] with your desired name for the frontend. After this you can configure how your NuxtJS application should look like. For the sake of this tutorial I choose following configuration:

NuxtJS Configuretion

While I selected default options for the most of the options, you have to make sure that you select Axios. This saves some steps as you do not have to set it up later yourself. I also select Universeal (SSR / SSG) as rendering mode to get SSR enabled by default. After NuxtJS finishes initializing your application you can start it by executing following commands:

cd [project-name]
npm run dev

When everything was configured correctly you can now access the NuxtJS application on: http://localhost:3000.

Querying our endpoints

Let us now connect the newly generated NuxtJS application with our previously generated endpoints.

There are multiple ways to achieve this, one possibility is to query single endpoints in the needed components or to query all endpoints in the Vue "store", called Vuex. Vuex is a state management pattern + library which integrates in Vue and is already preconfigured when working with NuxtJS. The Vuex store, which holds all available data can be access from every component in a Vue.js application. For this tutorial we are going to use Vuex to query our data.

To enable Vuex in NuxtJS, we just need to place a index.js file in the store folder, with following content.

export const state = () => ({
    
});

export const mutations = {};

export const actions = {};

export const getters = {};

These define the needed parts for Vuex to work properly. Now, let us break this down and insert the needed code in the four parts. State holds the available variables and their initial content. How you separate you want to save your endpoints is up to you but we define here a single variable which, after querying, should save our endpoint data Examples. So, we have to replace state with following code:

export const state = () => ({
    examples: {}
});

Mutations are methods, which change our defined state variables. We enter replace mutations with following code, which gives use the ability to change our previously defined state variable examples.

export const mutations = {
  setEndpoint(state, payload) {
    state.examples = payload;
  },
};

Actions is quite similar to mutations but defines the functions, which call the previously defined mutations. They should be the functions, which are called from your components directly but also where additional work should be done before then calling the appropriate mutations Here, we have to place following code:

export const actions = {
    async nuxtServerInit ({ dispatch }, { $axios }) {
        const payload = await $axios.$get([backend-IP][endpoint])
        dispatch('setEndpoint', payload)
    }
};

We have to replace [backend-IP] with our IP and port of our backend and [endpoint] with our specific endpoint. In our example this would be http://localhost:1337/examples

Finally, we change getters to the following, which allows our components to query our state variables and get their content:

export const getters = {
  examples: state => {
    return state.examples
  },
};

Everything combined results in following index.js file:

export const state = () => ({
    examples: {}
});

export const mutations = {
    setEndpoint(state, payload) {
        state.examples = payload;
    },
};

export const actions = {
    async nuxtServerInit ({ commit }, { $axios }) {
        const payload = await $axios.$get([backend-IP][endpoint])
        commit('setEndpoint', payload)
    }
};

export const getters = {
    examples: state => {
        return state.examples
    },
};

Consuming the endpoint data

While we now have the ability to get our data, we now can finally insert it into our components. For this we are going to generate a new component which we call Examples.vue and place in the folder components. For the sake of this tutorial, we are going to do this pretty minimally. So we enter following content into the newly created file:

<template>
  <div class="examples">
    <p>is visible: {{examples.isVisible}}</p>
    <p>budget: {{examples.Budget}}</p>
    <p>text: {{examples.text}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      examples: {}
    }
  },
  async created() {
    this.examples = await this.$store.getters.examples;
  }
}
</script>

<style>
</style>

This component is pretty straight forward. In the <script>...</script> block, we define a data variable called examples. This variable is the assigned to the with the result of our vuex-getters call in the created() method. created is the methods, which gets called before after the component is loaded and before it is rendered to the DOM. Therefore, we then render the data from our endpoint in the <template>...</template> block.

Lastly we have to call our component into the index.vue file located in the pages folder. We do this by replacing this file with the following code:

<template>
  <div class="container">
    <div>
      <Examples />
    </div>
  </div>
</template>

<script>
import Examples from '@/components/Examples.vue'

export default {
  components: {
    Examples
  }
}
</script>

If everything works as expected we end up with the following website:

Final Nuxt Site

Future Development

I strongly recommend, when working with Vue.js and Vuex to install the widely available Vue.js development tools. This tool neatly integrates in Firefox or Chrome and can be extremely convenient when working with Vue and especially Vuex to check the state of the store or components.

Vue.js devtools

This can be installed here:

Credits

All code for the initial setup can be found here: strapi-nuxt-boilerplate