David Lengweiler
Programming Tutorials and Guides
tutorial web application - 1 February 2021
Strapi and Nuxt Webapplication
Frontend: NuxtJS + Backend: Strapi
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 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.
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:
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:
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:
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.
This can be installed here:
Credits
All code for the initial setup can be found here: strapi-nuxt-boilerplate