As your Nuxt app grows, so does your backend. With time, your API evolves from a hand full of endpoints into an enormous jungle of resources. And you want to stay the king of the jungle! That means not losing track of the different (RESTful) endpoints and organizing them properly. Imagine you want to rename a resource called
So, a decent organization of your API calls in your frontend is necessary to stay sane. But how do we do that with Nuxt 2?
With Nuxt it seems to be bit more complicated to properly organize your API requests because you have to think about the server side in addition to the client side. You will probably retrieve data from your backend in
If you are not using the official Nuxt axios module by now, you should really switch over soon (read: now). Instead of importing axios everywhere, the module provides you a convenient interface on the client-side (
But is that it? Of course not! We found a way to centralize our configuration but that’s just one part of the puzzle.
Now the interesting part begins. For further context, a RESTful API will be assumed. The techniques in the following paragraphs will work for most of the APIs though but have to be adapted slightly.
For our example, we will use parts of the JSON Placeholder API as it covers all HTTP verbs and is not too complex.
Let’s take a look at the
If we consider other resources, for example
With this knowledge, we can try to abstract our API with a Plain Old Javascript Object (POJO) by creating a method for each verb. A
The design pattern we borrow us here is called Repository Pattern.
Let’s save this file to
export default {
create(payload) {},
show(id) {},
index() {},
update(payload, id) {},
delete(id) {}
}
Okay, so we have a basic scaffold for our API abstraction though we still have to fill the methods somehow. Now the
Another term crosses our way:
If you are depending on a library like axios you usually would import and use it like this:
import axios from 'axios'
export default {
async index() {
const result = await axios.get('...')
return result.data
}
}
This is fine for many use-cases, but for that one, the approach comes with several disadvantages:
While the former isn’t a huge issue in our case, the latter poses a problem for us. We know the dependency but we can’t actually
Instead, we inject the dependency which means we pass it as a parameter in a function (or to the constructor if you are using a
export default axios => ({
async index() {
const result = await axios.get('...')
return result.data
})
}
Boom, dependency injected! Let’s fill out our original scaffold and make use of the
export default $axios => ({
index() {
return $axios.$get('/posts')
},
create(payload) {
return $axios.$post(`/posts`, payload)
},
show(id) {
return $axios.$get(`/posts/${id}`)
},
update(payload, id) {
return $axios.$put(`/posts/${id}`, payload)
},
delete(id) {
return $axios.$delete(`/posts/${id}`)
}
})
Okay, so far so good. Currently, the
Instead of adding a second parameter to our default export we will transform the function to a higher order function (a function that returns a function):
export default $axios => resource => ({
index() {
return $axios.$get(`/${resource}`)
},
// ...
}
Now when we import our function somewhere, we can reuse it for many resources, but we only need to pass in the axios instance once:
import createRepository from '~/api/repository.js'
// First, call the function with the axios object
const $axios = getAxiosMagicallyFromSomewhere // we will find out how to get it further down the road
const repositoryWithAxios = createRepository($axios)
// Now you can create repositories and re-use the `repositoryWithAxios` function
const postRepository = repositoryWithAxios('posts')
const userRepository = repositoryWithAxios('users')
// ...
With this pattern, you don’t have to pass in the options again and again.
There we go. We have abstracted our API resources successfully and found a way to re-use that abstraction properly, but two problems are still unsolved.:
To solve both problems at once we utilize a Nuxt.js plugin. They are mostly used to add global Vue components or libraries but can be used in many other ways too. Also, they are evaluated before creating the root Vue instance.
Okay, let’s create a file at
If the plugin has a default export function, two parameters will be passed to that function: The Nuxt context and a method called
import createRepository from '~/api/repository.js'
export default (ctx, inject) => {
// Here we will do it
}
Inside the function, we have everything we need to make our API repositories available across all relevant parts of our Nuxt app and to pass the axios instance to our construct.
Because the context is available in the plugin default export method, we can pass in the axios instance without any problems:
import createRepository from '~/api/repository.js'
export default (ctx, inject) => {
const repositoryWithAxios = createRepository(ctx.$axios)
const repositories = {
posts: repositoryWithAxios('posts'),
users: repositoryWithAxios('users')
//...
}
}
Now the last (and mightiest) step has to be taken. Make the implementation available across our app so it can be used in components,
import createRepository from '~/api/repository.js'
export default (ctx, inject) => {
const repositoryWithAxios = createRepository(ctx.$axios)
const repositories = {
posts: repositoryWithAxios('posts'),
users: repositoryWithAxios('users')
//...
}
inject('repositories', repositories)
}
Then
We’ve completed our whole setup, so the only thing left is to actually use it. I’ve built a small live demo with CodeSandbox so you can directly play with it.
Make sure to check out the Vue API query plugin by Robson Tenório which provides an advanced query builder for your API. That package can definitely save you some work!
Using an API repository to organize your calls is a mighty tool to keep track of your endpoints, call them uniformly, reduce duplicate code and also to have an easier time debugging or changing them!
The idea of injecting objects in the context and injecting other dependencies in a Nuxt plugin is a recurring pattern that can be used in a variety of scenarios. Keep that one in mind!
Still have questions? No problem, tweet me at @TheAlexLichter, reach out on the Vue/Nuxt Discord or write me a mail (blog at lichter dot io).
Have you learned something by reading this post? Awesome, then I’ve reached my goal ? Also, please spread the word if you liked the article!
Originally published at November 22, 2018

I'm Alex, a German web engineering consultant and content creator. Helping companies with my experience in TypeScript, Vue.js, and Nuxt.js is my daily business.
More about meIt is time to recap the year 2023! I want to share my personal and professional journey through the last 365 days, as well as my goals for 2024. From moving to another country over jumping back into content creation. Hard facts, numbers and insights included - I promise!
Almost every Nuxt.js developer has seen the "evil grey page" that is displayed after a server-side error (for example a failing API call in asyncData or fetch without a try-catch block around it). Pretty sure many of you want to change it to make it fit to your design.