How to create generic service using Vue3 Js - vue.js

I am working on an application where I am trying to implement common functions which will call an API endpoint here is my implementation using vue3.
Page
<script>
import httpService from "./services/HttpService.vue";
export default {
data() {
return {
weather: undefined,
error : false,
errormessage : "",
};
},
methods : {
async fetchWeather(e) {
if (e.key == "Enter" && this.query) {
let {response} =await get(query,`${weather_url}forecast?city=`); //await axios.get(`${this.url_base}forecast?city=${this.query}`);
this.setResults(response.data);
}else if (e.key == "Enter" && !this.query){
this.error = true;
this.errormessage = 'Please enter name to search!';
}
},
setResults(res) {
if(res.isSuccessful === true){
this.error = false;
this.weather = res.response;
}else{
this.weather = undefined;
this.errormessage = res.response;
this.error = true;
}
}
},
};
</script>
Service
<script>
import axios from "axios";
export default async function GenericService() {
async function get(query,url) {
try {
let response = await axios.get(`${base_url}${url}${this.query}`);
return response;
} catch (err) {
throw new Error(err);
}
}
}
</script>
GlobalUrl
<script>
export const ConstantBaseUrl = {
base_url: "https://localhost:7005/",
};
export const EndPointUrl = {
weather_url : "api/weather/"
}
</script>
Here I want to achieve is there must be a common method that will be used to send a get request to my backend API. My code is not working and I know I am I might be missing something or doing it the wrong way because I am totally new to Vue3.js.

Related

How to call APi Service in component of Vue3

I am working on an application where I have created service js which I need to consume in different components of vue3. Here is my service code
const base_url = "https://localhost:7005/";
var apiObject = {
data: function() {
return {
response : undefined
};
},
methods: {
fetchContent: function(apiEndpoint) {
axios
.get(`${base_url}${apiEndpoint}`)
.then(res => {
this.response = res
})
.catch(e => {
this.errors.push(e);
});
}
}
};
Here is my component code. It is not working it gives me the error show in image below
<script>
import {fetchContent} from "../service/apiService";
export default {
data() {
return {
// url_base: "https://localhost:7005/api/weather/",
weather: undefined,
error : false,
errormessage : "",
searchHistory : []
};
},
methods : {
async fetchWeather(e) {
if (e.key == "Enter" && this.query) {
let {response} =await fetchContent(`api/weather/forecast?city=${query}`) //get(query,`${weather_url}forecast?city=`); //await axios.get(`${this.url_base}forecast?city=${this.query}`);
this.setResults(response.data);
}else if (e.key == "Enter" && !this.query){
this.error = true;
this.errormessage = 'Please enter name to search!';
}
},
setResults(res) {
if(res.isSuccessful === true){
this.error = false;
this.weather = res.response;
this.saveData(res.response)
}else{
this.weather = undefined;
this.errormessage = res.response;
this.error = true;
}
},
saveData(res){
this.searchHistory = JSON.parse(localStorage.getItem("SearchHistory"));
if(this.searchHistory == null){this.searchHistory = [];}
res.forEach(x => {
this.searchHistory.push(x);
});
localStorage.setItem("SearchHistory",JSON.stringify(this.searchHistory));
}
},
};
</script>
Image

Application error: a client-side exception has occurred (see the browser console for more information) .NEXT JS

In my extjs application , authorization works on the local host , but the page server gives an error : Application error: a client-side exception has occurred (see the browser console for more information) . They want a login, password are saved to the database, when logging in, a redirect to a page protected via axios is successfully performed, and there is an error there.
The cook's locale is saved correctly, but apparently cannot be counted from there.
I added a header (Access-Control-Allow-Origin) to the request, it didn't help.
My service :
import axios, {AxiosError} from "axios";
import { Router, useRouter } from "next/router";
import { useQuery } from "react-query";
const host = process.env.HOST || 'http://localhost:3000'
// axios instance
export const apiClient = axios.create({
baseURL: host + "/api",
withCredentials: true,
headers: {
"Content-type": "application/json"
},
});
export type Admin = {
id: string
login: string
}
export type RedirectError = {
redirectUrl: string
}
export const getSession = async () => {
const response = await apiClient.get<Admin>('getSession')
return response.data
}
export const useSession = () => {
const router = useRouter()
const { isLoading, error, data, isSuccess } = useQuery<Admin, AxiosError<RedirectError>>('sid', getSession)
if (error) router.push(error.response.data.redirectUrl)
return { isLoading, error, data, isSuccess }
}
My api/getSession:
import type { NextApiRequest, NextApiResponse } from 'next'
import checkSession from '../../src/services/checkCookie'
export default async function getSession(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'GET') {
try {
const sid = req.cookies['sid']
const admin = await checkSession(sid)
if (admin) {
const { id, login } = admin.admin
return res.send({ id, login })
}
const host = process.env.NODE_ENV === 'production' ? process.env.HOST : 'http://localhost:3000'
return res.status(401).send({ redirectUrl: host + '/admin/login' })
} catch (error) {
console.error(error)
res.status(500).send({ message: "" })
}
} else {
res.status(404).send({ message: "" })
}
}
checkSession in getSession api :
export default async function checkSession (token: string) {
// const token = req.cookies['sid']
if (typeof window === 'undefined' && token) {
const unsign = (await import('./signature')).unsign
const sessionToken = unsign(token, process.env.SECRET!)
if (sessionToken && typeof sessionToken === 'string') {
const db = (await import('../../prisma')).default
const session = await db.session.findUnique({ where: { sessionToken },
include: { admin: true } })
if (session) {
return { admin: session.admin }
}
}
}
}
Page with axios
import { NextPage } from "next"
import TableService from "../src/component/TableService"
import AdminLayout from "../src/component/admin/AdminLayout"
import { Admin, getSession, RedirectError, useSession } from "../src/services/apiClient"
import { useRouter } from "next/router"
import { CircularProgress } from "#mui/material"
const AdminTable: NextPage = () => {
const router = useRouter()
const {isLoading, error, data, isSuccess } = useSession()
if (isLoading) return <CircularProgress />
return (
<>
{data && <div>{data.login}</div>}
{isSuccess &&
<AdminLayout title="">
<TableService />
</AdminLayout>
}
{isLoading && <p>Loading..</p>}
{error && <p>Error occurred!</p>}
</>
)
}
export default AdminTable

Nuxt fetch hook api can't access the api folder

I have an api that I call in my fetch() hook:
pages/pageOne.vue:
async fetch() {
const { store, error } = this.$nuxt.context;
try {
await store.dispatch("folderOne/apiOne", null, { root: true });
} catch (e) {
error({
message: "error"
});
}
}
then in my store I have apiOne action setup like this:
store/folderOne/index.js:
//importing the api file here
const actions = {
apiOne({ commit }) {
apiFile.apiOne().then(data => {
if(data && data.status === 200){
// then commit
}
})
.catch(error => {
console.log(error);
});
},
}
Then I have a file for my APIs setup like this:
api/apiFile.js:
import axios from "axios";
const httpClient = axios.create({
headers: {
key: 'value
}
});
const baseUrl = process.env.config.baseUrl;
export default {
apiOne() {
return httpClient.get(`${baseUrl}values);
},
}
It doesn't work. But when I call the same apiOne in a #click method it works. Anyone knows what's is wrong and how can I fix it?
your store is and should be a global construct of your application.
it means when you call for a action and your setup is correct, you don't have to handle directives/folders by yourself.
in your case all you should do is dispatch the action in your component like this:
async fetch() {
const { store, error } = this.$nuxt.context;
try {
await store.dispatch("apiOne", null, { root: true });
} catch (e) {
error({
message: "error"
});
}
}

how can i use async and await in action object in vuex?

I'm gonna use an API and take it off some information, I use async/ await in mutations but as you know it's not standard that we used async data in mutation, and we have to use it in actions but how we can do it?
here my vuex codes:
import axios from "axios";
const state = {
token: "hjOa0PgKqC7zm86P10F3BQkTuLsEV4wh",
posts: [],
pending: true,
error: false,
}
const mutations = {
async getDataFromApi(state) {
try {
const res = await axios.get(
`https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=${state.token}`
);
if (res.status == 200) {
state.posts = res.data;
state.error = false;
}
} catch (e) {
state.posts = null;
state.error = e;
}
state.pending = false;
},
};
const actions = {
showData({
commit
}) {
commit("getDataFromApi");
},
}
and here vuejs codes that I used in the component :
<script>
import { mapState } from "vuex";
export default {
name: "Home",
mounted() {
this.getDataFromApi();
},
computed: {
...mapState(["pending", "error", "posts"]),
},
methods: {
getDataFromApi() {
this.$store.dispatch("showData");
},
},
};
</script>
It works perfectly in mutation but for standards, how can use this in action instead of mutation?
Well, actually it is pretty similar to what you have done so far :
const mutations = {
getDataFromApi(state, data) {
state.posts = data;
state.error = false;
state.pending = false;
},
setError(state, error) {
state.error = error;
state.posts = null;
state.pending = false;
},
};
const actions = {
async showData({ commit }) {
try {
const res = await axios.get(
`https://api.nytimes.com/svc/movies/v2/reviews/picks.json?api-key=${state.token}`
);
if (res.status == 200) {
commit("getDataFromApi", res.data);
} else {
commit("setError", new Error("Something went wrong."));
}
} catch (e) {
commit("setError", e);
}
},
};

Too many requests when controlling spinner show/hide from axois interceptors

I have an SPA written in Vue (Webpack) where I want to control the visibility of a spinner based on whether or not the app is currently handling an HTTP request or a response.
Following some tutorials, I came up with the event bus scheme and did this:
Created eventBus.js:
import Vue from 'vue';
export const eventBus = new Vue();
I'm setting my axios interceptors in the created() hook of App.vue. Here's what the necessary functions look like in that component:
data() {
return {
showLoader: false
};
},
created(){
this.setAxiosInterceptors();
// some code removed //
}
},
mounted() {
eventBus.$on('show-loader', () => {
this.showLoader = true;
});
eventBus.$on('hide-loader', () => {
this.showLoader = false;
});
},
methods: {
setAxiosInterceptors() {
var tokenCookieName = this.$store.getters.getCookieNames.apiToken;
var cookieDefaultValue = this.$store.getters.getCookieDefaultValue;
// token expired middleware
this.axios.interceptors.response.use(response => {
var data = response.data;
if(data.info.api_token) {
this.$cookie.set(tokenCookieName, data.info.api_token);
}
if(data.status == 'error' && data.info.login_failed) {
this.$cookie.set(tokenCookieName, cookieDefaultValue);
window.location = '/'; // not possible to use Vue router here
}
eventBus.$emit('hide-loader');
return response;
},
error => {
eventBus.$emit('hide-loader');
console.log('Response interception failed!');
return Promise.reject(error);
});
// attach API token middleware
this.axios.interceptors.request.use(config => {
var apiToken = this.$cookie.get(tokenCookieName);
if (!apiToken) {
apiToken = cookieDefaultValue;
}
config.headers.Authorization = 'Bearer ' + apiToken;
eventBus.$emit('show-loader');
return config;
},
error => {
eventBus.$emit('hide-loader');
console.log('Request interception failed!');
return Promise.reject(error);
}
);
}
}
Please ignore some of the code that isn't relevant to the problem, but I wanted to show how things are set up. Problem is, as soon as I visit my home page, the app keep making the startup GET requests over and over, until my server returns a 429 error.
Interestingly, in my eventBus.$on handlers, if I just do a console.log, this behavior doesn't appear (of course, the spinner doesn't work as well) but as soon as I change a variable or call a vuex action, this infinite reloading starts.
Any clue?
In the main.js file
Vue.prototype.$axios = axios.create(
{
headers:
{
'Content-Type': 'application/json',
},
baseURL: process.env.API_URL
}
);
Vue.prototype.$axios.interceptors.request.use(
config =>
{
eventBus.$emit('show_spin');
let token = getTokenID();
if(token && token.length) config.headers['Authorization'] = token;
return config;
},
error =>
{
eventBus.$emit('hide_spin');
if (error.status === 401) VueRouter.push('/login');
else throw error;
}
);
Vue.prototype.$axios.interceptors.response.use(
response =>
{
eventBus.$emit('hide_spin');
return response;
},
error =>
{
eventBus.$emit('hide_spin');
return new Promise(function(resolve,reject)
{
if (error.config && error.response && error.response.status === 401 && !error.config.__isRetry)
{
myVue.refreshToken(function()
{
error.config.__isRetry = true;
error.config.headers['Authorization'] = getTokenID();
myVue.$axios(error.config).then(resolve,reject);
},function(flag) // true = invalid session, false = something else
{
if(process.env.NODE_ENV === 'development') console.log('Could not refresh token');
if(getUserID()) myVue.showFailed('Could not refresh the Authorization Token');
reject(flag);
});
}
else throw error;
});
}
);
let myVue = new Vue(
{
el: '#app',
data: function()
{
return {
spin_visible: 0, // dynamically show/hide spinner
};
},
created: function()
{
eventBus.$on('show_spin', this.showSpin);
eventBus.$on('hide_spin', this.hideSpin);
},
methods:
{
showSpin: function()
{
this.spin_visible++;
},
hideSpin: function()
{
if(this.spin_visible>0) this.spin_visible--;
},
....
and then in App.vue
<template>
<router-view/>
<div class="spinner" v-show="$root.spin_visible">
<!-- define your spinner here -->
</div>
</template>