vuejs2 get return network error - api

I am trying to access a prestashop API with vuejs2
<script>
import axios from 'axios'
export default {
data () {
return {
get: [],
errors: []
}
},
created () {
axios({
method: 'get',
url: 'https://myprestashopsite.com/api/categories/?ws_key=J***************Z&filter[id_parent]=5&output_format=JSON'
}).then(response => {
this.get = response.data
})
.catch(e => {
this.errors.push(e)
})
}
}
In the web developer console of mozilla I see that my request return a 200 with the data in the response. But I get "Error: Network Error app.js%20line%201266%20%3E%20eval:15:15" catch as an error.
I tried with another API and it worked, so I guess it comes from the prestashop api. (prestashop version 1.7.3.0)
Is there a way to fix this ?

The problem seems to come from axios. I had to add a rule to the server.
I found the solution to this on this thread :
https://github.com/axios/axios/issues/853
There are other solutions I didn't try in this thread if mine doesn't work.
how to add the rule : https://enable-cors.org/server.html

Related

Nuxt.js - The best place for API calls

I'm new to Vue.js Nuxt and all front-end stuff.
I have a question about API calls. I'm not sure what is the right way, the best practice here.
I have a store. In that store, I have actions that are calling my API and sets state eg.
async fetchArticle({ state, commit }, uuid) {
const response = await this.$axios.get(`articles/${uuid}/`)
commit('SET_ARTICLE', response.data)
},
And that is fine it is working for one component.
But what if I want to just fetch the article and not changing the state.
To be DRY first thing that comes to my mind is to create the service layer that is fetching the data and is used where it is needed.
Is it the right approach? Where can I find some real-world examples that I can take inspiration from?
Using the repository pattern to abstract your API is definitely a good idea! Whether you use the #nuxtjs/axios module or the #nuxt/http module, you can pass either instance to your repository class/function. Below a real world example of an abstracted "repository.js" file.
export default $axios => resource => ({
index() {
return $axios.$get(`/${resource}`)
},
create(payload) {
return $axios.$post(`/${resource}`, payload)
},
show(id) {
return $axios.$get(`/${resource}/${id}`)
},
update(payload, id) {
return $axios.$put(`/${resource}/${id}`, payload)
},
delete(id) {
return $axios.$delete(`/${resource}/${id}`)
}
})
You can then create a plugin to initialize all different kinds of repositories for your endpoints:
import createRepository from '~/path/to/repository.js'
export default (ctx, inject) => {
const repositoryWithAxios = createRepository(ctx.$axios)
const repositories = {
posts: repositoryWithAxios('posts'),
users: repositoryWithAxios('users')
//...
}
inject('repositories', repositories)
}
Further read: Organize and decouple your API calls in Nuxt.js
I will an example of a service layer implementation for my portfolio to create my dashboard that shows some statics about my github and stackoverflow profiles, to do this i created a folder called services inside the project root :
pages
services
|_AxiosConfig.js
|_GitHubService.js
|_StackoverflowService.js
...
in the AxiosConfig.js file i put i created an axios instance with its configuration :
import axios from 'axios';
const clientAPI = url =>
axios.create({
baseURL: url,
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
export default clientAPI;
then in my GitHubService.js i imported that axios instance called clientAPI which i used to my requests :
import clientAPI from './AxiosConfig';
const baseURL = 'https://api.github.com';
export default {
getUser(name) {
return clientAPI(baseURL).get('/users/' + name);
},
getRepos(name){
return clientAPI(baseURL).get('/users/' + name+'/repos');
},
getEvents(name,page){
return clientAPI(baseURL).get('/users/' + name+'/events?per_page=100&page='+page);
},
getLastYearCommits(name,repo){
return clientAPI(baseURL).get('/repos/' + name+'/'+repo+'/stats/commit_activity');
}
};
then in my page i used asyncData hook to fetch my data :
import GitHubService from '../../services/GitHubService'
export default {
...
async asyncData({ error }) {
try {
const { data } = await GitHubService.getUser("boussadjra");
const resRepos = await GitHubService.getRepos("boussadjra");
return {
user: data,
repos: resRepos.data
};
} catch (e) {
error({
statusCode: 503,
message: "We cannot find the user"
});
}
}
I wanted to use axios in my service/service.js file, so instead of passing axios, I accessed it directly like this:
export default {
async fetchArticle() {
let response = await $nuxt.$axios.$get('/api-url')
return response
},
}
In Nuxt, if you want to just get the data without keeping it in your store, you could use the asyncData function, which asynchronously loads data (from API calls and the like) and pushes it into the component's data object before rendering.

Nuxt reload api data fails

Need some help in solving a reload problem.
I fetch data via service:
import axios from 'axios'
const apiClient = axios.create({
baseURL: 'www.domain/api/v1',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
and
export default {
getCompanies() {
return apiClient.get('/companies')
},
in store:
export const actions = {
fetchCompanies({ commit }) {
return CompanyService.getCompanies().then(response => {
commit('SET_COMPANIES', response.data)
})
},
in pages/companies:
async fetch({ store, error }) {
try {
await store.dispatch('company/fetchCompanies')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch Companies at this time'
})
}
},
works fine, but no data on page reload.
Some help would be great.
If you are using it on a pagination component the reason it is not reloading is that fetch it will be called server-side once and in the client-side just when navigating to further routes.
The fetch method is not called on query string changes by default. If you want to change this behavior, for example when building a pagination component, you can setup parameters that should be listened to through the watchQuery property of your page component. Learn more on the API watchQuery page.
Looks Nuxt documentation: https://nuxtjs.org/api/pages-fetch/
You can use watchQuery to fix this.
https://nuxtjs.org/api/pages-watchquery

AWS Amplify breaks binary posts to AWS API-Gateway

I'm creating a web application using React / Aws Amplify as the front end and AWS API-Gateway and S3 in the back end and Cognito as our user authentication. I have a page where the user needs to submit a form and a file. I was able to set this up for text files but once I started to work on binary files bad things happened.
I build the API in AWS and tested it using Postman as well as Curl and I'm able to post binary files. When I make the call through Amplify it stops working. I can make the call through Axios but then I need to turn off the Authentication, hence why I'm trying to do this through amplify. I also do not want to use amplify storage as it does not meet my needs. What typically happens is the file size is larger then the file sent and when I download it out of S3 it does not work any longer.
import React, { Component } from "react";
import "./Dashboard.css";
import { API } from "aws-amplify";
import { saveAs } from 'file-saver';
import axios from 'axios';
export default class Home extends Component {
uploadLambda = async (event) => {
//This one work if I turn off User Authentication
let file = event.target.files[0];
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = async () => {
try
{
return axios({
method: 'POST',
url: 'https://XXXXXXXXXX.execute-api.us-east-1.amazonaws.com/dev/upload',
headers: {
'Content-Type': 'application/pdf'
},
data: reader.result
});
}
catch(e)
{
console.log(e);
}
}
}
uploadImageLambda = async(event) => {
//This is the one I'm trying to get to work with binary files
var file_name = event.target.files[0].name;
console.log('Saving File Via Lambda: ' + file_name);
var reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
//reader.readAsBinaryString(event.target.files[0]);
//reader.readAsArrayBuffer(event.target.files[0]);
reader.onload = async () =>
{
try
{
/**
Someone suggested this but it does not fix the problem
let encoded = reader.result.toString().replace(/^data:(.*,)?/, '');
if ((encoded.length % 4) > 0) {
encoded += '='.repeat(4 - (encoded.length % 4));
}
console.log(encoded);
//"isBase64Encoded": "true",
**/
return await API.post("lambdadocshell", 'upload', { 'headers': { 'Content-Type': 'application/pdf', }, 'body': reader.result });
}
catch (e)
{
console.log(e);
}
}
}
render() {
return (
<div className="FileTest">
<h1>Upload A File</h1>
<form onSubmit={this.handleSubmit} enctype="multipart/form-data">
Select File: <input type="file" onChange={this.uploadLambda} />
</form>
</div>
);
}
}
In the code above you can see 2 upload functions, both hit the same API-Gateway. uploadLambda works but only if authentication on the API-Gateway is turned off. uploadImageLambda does not work regardless of authentication. We do use the Amplify in a number of other pages to move JSON back and forth to the API without issues. You can also see commented code as we tried a number of different ways to get amplify to work.
After talking with AWS support, they said that amplify apparently does a JSON.stringify to the data which then increases the length of the file. Currently there does not seem to be a workaround for this issue. As such they suggested that I use Axios to make the request to API Gateway. Hopefully this will be resolved in the future.

Unit testing HTTP request with Vue, Axios, and Mocha

I'm really struggling trying to test a request in VueJS using Mocha/Chai-Sinon, with Axios as the request library and having tried a mixture of Moxios and axios-mock-adaptor. The below examples are with the latter.
What I'm trying to do is make a request when the component is created, which is simple enough.
But the tests either complain about the results variable being undefined or an async timout.
Am I doing it right by assigning the variable of the getData() function? Or should Ireturn` the values? Any help would be appreciated.
Component
// Third-party imports
import axios from 'axios'
// Component imports
import VideoCard from './components/VideoCard'
export default {
name: 'app',
components: {
VideoCard
},
data () {
return {
API: '/static/data.json',
results: null
}
},
created () {
this.getData()
},
methods: {
getData: function () {
// I've even tried return instead of assigning to a variable
this.results = axios.get(this.API)
.then(function (response) {
console.log('then()')
return response.data.data
})
.catch(function (error) {
console.log(error)
return error
})
}
}
}
Test
import Vue from 'vue'
import App from 'src/App'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
let mock = new MockAdapter(axios)
describe('try and load some data from somewhere', () => {
it('should update the results variable with results', (done) => {
console.log('test top')
mock.onGet('/static/data.json').reply(200, {
data: {
data: [
{ id: 1, name: 'Mexican keyboard cat' },
{ id: 2, name: 'Will it blend?' }
]
}
})
const VM = new Vue(App).$mount
setTimeout(() => {
expect(VM.results).to.be.null
done()
}, 1000)
})
})
I am not sure about moxios mock adaptor, but I had a similar struggle. I ended up using axios, and moxios, with the vue-webpack template. My goal was to fake retreiving some blog posts, and assert they were assigned to a this.posts variable.
Your getData() method should return the axios promise like you said you tried - that way, we have some way to tell the test method the promise finished. Otherwise it will just keep going.
Then inside the success callback of getData(), you can assign your data. So it will look like
return axios.get('url').then((response) {
this.results = response
})
Now in your test something like
it('returns the api call', (done) => {
const vm = Vue.extend(VideoCard)
const videoCard = new vm()
videoCard.getData().then(() => {
// expect, assert, whatever
}).then(done, done)
)}
note the use of done(). That is just a guide, you will have to modify it depending on what you are doing exactly. Let me know if you need some more details. I recommend using moxios to mock axios calls.
Here is a good article about testing api calls that helped me.
https://wietse.loves.engineering/testing-promises-with-mocha-90df8b7d2e35#.yzcfju3qv
So massive kudos to xenetics post above, who helped in pointing me in the right direction.
In short, I was trying to access the data incorrectly, when I should have been using the $data property
I also dropped axios-mock-adaptor and went back to using moxios.
I did indeed have to return the promise in my component, like so;
getData: function () {
let self = this
return axios.get(this.API)
.then(function (response) {
self.results = response.data.data
})
.catch(function (error) {
self.results = error
})
}
(Using let self = this got around the axios scope "problem")
Then to test this, all I had to do was stub the request (after doing the moxios.install() and moxios.uninstall for the beforeEach() and afterEach() respectively.
it('should make the request and update the results variable', (done) => {
moxios.stubRequest('./static/data.json', {
status: 200,
responseText: {
data: [
{ id: 1, name: 'Mexican keyboard cat' },
{ id: 2, name: 'Will it blend?' }
]
}
})
const VM = new Vue(App)
expect(VM.$data.results).to.be.null
VM.getData().then(() => {
expect(VM.$data.results).to.be.an('array')
expect(VM.$data.results).to.have.length(2)
}).then(done, done)
})

Attaching data (body) to $http.delete event in VueJS

I have the following method in my Vue.JS component:
removeItems (itemsArray) {
this.$http.delete(this.apiUrl, {items : itemsArray})
.then((response) => {
this.msg = response.msg;
});
}
In vue-resource 0.8.0 everything worked fine. After upgrading to 1.0.3 it doesn't. I found in release notes that they deleted body from GET request, which makes sense, but why did the DELETE request stop working?
If they disabled specifying body explicitly in the DELETE request, how do I add it?
Found a solution. Simply add {body:data} to the request:
removeItems (itemsArray) {
this.$http.delete(this.apiUrl, {body: {items : itemsArray}})
.then((response) => {
this.msg = response.msg;
});
}