Giedrius
Giedrius

Reputation: 1390

How to structure API code in a Vue Single-Page App?

I'm building a fairly large SPA using Vue (and Laravel for RESTful API). I'm having a hard time finding resources about this online - what's a good practice to organise the code that communicates with the server?

Currently I have src/api.js file, which uses axios and defines some base methods as well as specific API endpoints (truncated):

import axios from 'axios';
axios.defaults.baseURL = process.env.API_URL;

const get = async (url, params = {}) => (await axios.get(url, { params }));
const post = async (url, data = {}) => (await axios.post(url, data));

export const login = (data) => post('users/login', data);

And then in my component, I can do

...
<script>
import { login } from '@/api';

...
methods: {
   login() {
       login({username: this.username, password: this.password})
           .then() // set state
           .catch() // show errors
   }
}
</script>

Is this a good practice? Should I split up my endpoints into multiple files (e.g. auth, users, documents etc.)? Is there a better design for this sort of thing, especially when it comes to repetition (e.g. error handling, showing loading bars etc.)?

Thanks!

Upvotes: 0

Views: 1880

Answers (2)

cloake
cloake

Reputation: 56

If you're just using Vue and expect to be fetching the same data from the same component every time, it's generally idiomatic to retrieve the data and assign it using the component's mounted lifecycle hook, like so:

<template>
<h1 v-if="name">Hello, {{name}}!</h1>
</template>

<script>
export default {
  data() {
    return {
      name: '',
    }
  },

  mounted() {
    axios.get('https://example.com/api')
      .then(res => {
        this.name = res.data.name;
      })
      .catch(err => 
        // handle error
      );
  },
};
</script>

If you're going to be using Vuex as mentioned in one of your comments, you'll want to put your API call into the store's actions property.

You'll end up with a Vuex store that looks something like this:

const store = new Vuex.Store({
  state: {
    exampleData: {},
  },

  mutations: {
    setExampleData(state, data) {
      state.exampleData = data;
    },
  },

  actions: {
    async getExampleData() {
      commit(
        'setExampleData',
         await axios.get('https://www.example.com/api')
          .then(res => res.data)
          .catch(err => {
            // handle error
          });
      );
    },
  }
});

Of course, breaking out your state, actions, and mutations into modules as your app grows is good practice, too!

Upvotes: 1

Bergur
Bergur

Reputation: 4067

If you use Vue CLI it will setup a basic project structure. With a HelloWorld component. You will want to break your vue app into components. Each component should have a defined role that ideally you could then unit test.

For example lets say you want to show list of products then you should create a product list component.

<Products :list="products" />

In your app you would do something like

data() {
  return {
    prodcuts: []
  }
},
mounted() {
  axios.get('/api/products').then(res => {
    this.products = res.data
  })
}

Whenever you see something that "is a block of something" make a component out of it, create props and methods and then on the mounted hook consume the api and populate the component.

Upvotes: 0

Related Questions