vue bestpractice api handling - vue.js

Been reading the docs and googling around for best practice to handle api calls in bigger projects without luck (or ateast not what Im searching for).
I want to create a service / facade for the backend that I can load in every component that needs it. For exampel.
I want to fetch historical data for weather in a service so in every component I need this I can just load the weather-serivce and use a getter to fetch the wanted data. I would like to end up with something like below. But I dosent get it to work. So I wonder, what is best practice for this in vue.js?
import WeatherFacade from './data/WeatherFacade.vue'
export default {
name: 'Chart',
created () {
console.log(WeatherFacade.getWeather())
},
components: {
WeatherFacade
}
}
ps. using vue 2.1.10

It could be easily done by creating some external object that will hold those data and module bundling.What I usually do in my projects is that I create services directory and group them in order I want.
Let's break it down - services/WeatherFascade.js (using VueResource)
import Vue from 'vue'
export default {
getWeather() {
return Vue.http.get('api/weather')
}
}
If you have to pass some dynamic data such as ID, pass it as just parameter
import Vue from 'vue'
export default {
getWeather(id) {
return Vue.http.get(`api/weather/${id}`)
}
}
Then in your component you can import this service, pass parameters (if you have them) and got data back.
import WeatherFascade from '../services/WeatherFascade'
export default {
data() {
return {
weatherItems: []
}
},
created() {
this.getWeatherData()
},
methods: {
getWeatherData() {
WeatherFascade.getWather(// you can pass params here)
.then(response => this.weatherItems = response.data)
.catch(error => console.log(error))
}
}
}
You can use any library for that you like, for instance axios is cool.

Related

How to use Toasted inside an export default {}

I'm trying to use the package Toasted but I'm having a hard time understading how to use it.
I have a package called TreatErrors.js and I call this package to handle all errors from my application based on HTTP code returned by API a restfull API.
TreatErrors.js
import toasted from 'vue-toasted';
export default {
treatDefaultError(err){
let statusCode = err.response.status;
let data = err.response.data;
for(let field in data.errors){
if (data.errors.hasOwnProperty(field)) {
data.errors[field].forEach(message => {
toasted.show(message);
})
}
}
if(statusCode === 401){
toastr.error('Your token has expired. Please logout and login again to retrieve a new token');
}
return null;
},
}
and I'm tryin to call Toasted from within this package but I'm getting vue_toasted__WEBPACK_IMPORTED_MODULE_2___default.a.show is not a function. Any idea how I can use this Toasted inside of my own defined package?
The vue-toasted plugin must be registered with Vue first:
import Toasted from 'vue-toasted';
Vue.use(Toasted); // <-- register plugin
Then, your module could use it via Vue.toasted.show(...):
// TreatErrors.js
export default {
treatDefaultError(err) {
Vue.toasted.show(err.message);
}
}
And your Vue components could also use it via this.$toasted.show(...):
// Foo.vue
export default {
methods: {
showError(err) {
this.$toasted.show(err.message);
}
}
}

where to store global object in quasar framework

I'm re-writing my old app using Quasar Framework which is based on Vue, and I have a piece of code (class) which encapsulates websocket functionality.
It is a fairly simple concept: user travels from page to page in the app, but if he receives a message he can see a toast message/reply or a counter of unread messages increments.
I'm a little bit lost in the Quasar (Vue) architecture and here is my question:
Where would I store a global object which communicates with outside world, exists as long as the application exists and accessible from anywhere?
I read documentation of Quasar (Vue) but I still don't know where to put it. Vuex doesn't look right since it is not a state of the app. It is more like a faceless component.
Does it mean that I should use a plugin or Vue.prototype or a global mixin or something else?
I appreciate if someone can share their experience and a piece of code describing how to initialize and access this object's methods.
in my opinion:
Method 1. Use quasar plugin base on Vue prototype (sure you knew it):
plugins/foo.js
const fooModule = {
a: 1,
doTest() { console.log('doTest') }
};
export default ({Vue}) => {
Vue.prototype.$foo = fooModule;
}
quasar.conf.js
plugins: [
'i18n',
'axios',
'foo',
],
component bar.vue:
methods: {
test () { this.$foo.doTest() }
}
Method 2. Just use js module
Because js module is singleton. Wherever you import a js module, it all points to the same pointer.
So just have GlobalTest.js:
export default {
a: 1,
inc() { this.a = this.a + 1 }
}
And test1.js:
import GlobalTest from '/path/to/GlobalTest'
console.log(GlobalTest.a); // output 1
console.log(GlobalTest.inc()); // inc
And test2.js:
import GlobalTest from '/path/to/GlobalTest'
console.log(GlobalTest.a); // Assuming this was called after test1.js: output 2
I used quasar cli but I just consider quasar as a UI lib.
--- Updated ---
It is a fairly simple concept: user travels from page to page in the app, but if he receives a message he can see a toast message/reply or a counter of unread messages increments.
Depend on the requirements, If you need "reactive" you should use Vuex (best built-in reactive lib) + split the app state into modules,
but I only use Vuex when I need "reactive" and avoid it when I just need to read & write the value.
// ~/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
let store
export default function (/* { ssrContext } */) {
/* eslint-disable no-return-assign */
return store = new Vuex.Store({
modules: {...},
strict: process.env.DEV
})
}
export function ensureClientStoreExists () {
if (process.env.SERVER) {
return new Promise(() => { /* won't resolve */ })
}
if (process.env.CLIENT) {
if (store) {
return Promise.resolve(store)
}
return new Promise(resolve => {
setTimeout(resolve) // Avoid 'Maximum call stack size exceeded'
}).then(ensureClientStoreExists)
}
}
// Anywhere
import { ensureClientStoreExists } from '~/store/'
ensureClientStoreExists().then(store => {
console.log(store.state)
store.dispatch('XXX/YYY')
})

Vue.js best approach to organize API endpoints?

On my components I am having to access my endpoints (API Gateway/Lambda), this is currently requiring me to hardcode this on a per-component level. Obviously not ideal, haha.
Here is an example of what I have now on a Vue component:
async mounted() {
axios.get('https://XXXXXXX.execute-api.us-east-1.amazonaws.com/{environment}/{endpoint_name}')
.then(response => {
this.data = response.data;
})
}
Ideally I am trying to find an elegant way of populating these axios.get() sections, so I can have a main reference for the execution id and environment (dev/qa/prod/etc).
I am new to Vue.js, so I am struggling to find the ideal approach. Currently my thought would be to create string that pulls from main.js then adds onto the .get url. Such as .get(LAMBDA_URL + 'test'), ideas?
First of all, you can abstract away all api calls to their own file, somewhere in an api folder. You can then make your own get, post, put and delete methods that perform some basic boilerplate stuff like prepending the lambda url and parsing common things such as the status code.
You can go further by only calling these api endpoints in your vuex store if you have one. The nice part of that is that your components are no longer concerned where they get the data from. The implementation of getting the data is all in some fetch action somewhere in your store. Your components will use a getter to show things.
// api/index.js
import { apiUrl } from '#/config';
function apiRequest(method, url, data, whatever) {
return axios({
method,
data,
url: `${apiUrl}/${url}`
// etc
});
}
export function get(url, data, whatever) {
return apiRequest('get', url, data, whatever);
}
// etc
// Component
<template>
<div>
{{ data }}
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: 'my-component',
computed: {
...mapGetters({
data: 'animals/birds'
})
},
mounted () {
this.$store.dispatch('animals/fetchBirds');
}
}
</script>

Decentralizing functions in vuejs

Am from Angular2 whereby i was used to services and injection of services hence reusing functions how do i achieve the same in vuejs
eg:
I would like to create only one function to set and retrieve localstorage data.
so am doing it this way:
In my Login Component
this.$axios.post('login')
.then((res)=>{
localstorage.setItem('access-token', res.data.access_token);
})
Now in another component when sending a post request
export default{
methods:{
getvals(){
localstorage.getItem('access-token') //do stuff after retrieve
}
}
}
Thats just one example, Imagine what could happen when setting multiple localstorage items when retrieving one can type the wrong key.
How can i centralize functionality eg: setting token(in angular2 would be services)
There are a few different ways to share functionality between components in Vue, but I believe the most commonly used are either mixins or custom modules.
Mixins
Mixins are a way to define reusable functionality that can be injected into the component utilizing the mixin. Below is a simple example from the official Vue documentation:
// define a mixin object
var myMixin = {
created: function () {
this.hello()
},
methods: {
hello: function () {
console.log('hello from mixin!')
}
}
}
// define a component that uses this mixin
var Component = Vue.extend({
mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"
Custom module
If there are a lot of shared functionality with a logical grouping it might make sense to instead create a custom module, and import that where you need it (like how you inject a service in angular).
// localStorageHandler.js
const localStorageHandler = {
setToken (token) {
localStorage.setItem('access-token', token)
},
getToken () {
localstorage.getItem('access-token')
}
}
export default localStorageHandler
And then in your component:
// yourcomponent.vue
import localStorageHandler from 'localStorageHandler'
export default{
methods:{
getvals(){
const token = localStorageHandler.getToken()
}
}
}
Modules are using the more modern syntax of JavaScript, which is not supported in all browsers, hence require you to preprocess your code. If you are using the vue-cli webpack template it should work out of the box.

How to structure api calls in Vue.js?

I'm currently working on a new Vue.js application. It depends heavily on api calls to my backend database.
For a lot of things I use Vuex stores because it manages shared data between my components. When looking at other Vue projects on github I see a special vuex directory with files that handles all the actions, states and so on. So when a component has to call the API, it includes the actions file from the vuex directory.
But, for messages for example, I don't want to use Vuex because those data is only important for one specific view. I want to use the component specific data here. But here is my problem: I still need to query my api. But I shouldn't include the Vuex actions file. So in that way I should create a new actions file. This way I have a specific file with api actions for vuex and for single components.
How should I structure this? Creating a new directory 'api' that handles actions for both vuex data and component-specific data? Or separate it?
I am using axios as HTTP client for making api calls, I have created a gateways folder in my src folder and I have put files for each backend, creating axios instances, like following
myApi.js
import axios from 'axios'
export default axios.create({
baseURL: 'http://localhost:3000/api/v1',
timeout: 5000,
headers: {
'X-Auth-Token': 'f2b6637ddf355a476918940289c0be016a4fe99e3b69c83d',
'Content-Type': 'application/json'
}
})
Now in your component, You can have a function which will fetch data from the api like following:
methods: {
getProducts () {
myApi.get('products?id=' + prodId).then(response => this.product = response.data)
}
}
Similarly you can use this to get data for your vuex store as well.
Edited
If you are maintaining product related data in a dedicate vuex module,
you can dispatch an action from the method in component, which will internally call the backend API and populate data in the store, code will look something like following:
Code in component:
methods: {
getProducts (prodId) {
this.$store.dispatch('FETCH_PRODUCTS', prodId)
}
}
Code in vuex store:
import myApi from '../../gateways/my-api'
const state = {
products: []
}
const actions = {
FETCH_PRODUCTS: (state, prodId) => {
myApi.get('products?id=' + prodId).then(response => state.commit('SET_PRODUCTS', response))
}
}
// mutations
const mutations = {
SET_PRODUCTS: (state, data) => {
state.products = Object.assign({}, response.data)
}
}
const getters = {
}
export default {
state,
mutations,
actions,
getters
}
Note: vue-resource is retired ! Use something else, such as Axios.
I'm using mostly Vue Resource.I create services directory, and there put all connections to endpoints, for e.g PostService.js
import Vue from 'vue'
export default {
get(id) {
return Vue.http.get(`/api/post/${id}`)
},
create() {
return Vue.http.post('/api/posts')
}
// etc
}
Then in my file I'm importing that service and create method that would call method from service file
SomeView.vue
import PostService from '../services/PostService'
export default {
data() {
item: []
},
created() {
this.fetchItem()
},
methods: {
fetchItem() {
return PostService.get(to.params.id)
.then(result => {
this.item = result.json()
})
}
}
}
Based on concept of Belmin Bedak`s answer, i have wrapped it all into a simple library:
https://github.com/robsontenorio/vue-api-query
You can request your API like this:
All results
// GET /posts?filter[status]=ACTIVE
let post = await Post
.where('status', 'ACTIVE')
.get()
Specific result
// GET /posts/1
let post = await Post.find(1)
Editing
// PUT /posts/1
post.title = 'Awsome!'
post.save()
Relationships
// GET /users/1
let user = await User.find(1)
// GET users/1/posts
let posts = await user
.posts()
.get()