I using Vue 3 and Quasar Framework (latest).
I create a Api class as wrapper for Axios with some methods (get, post, etc...).
And i'm need to intercept this methods calling.
For this, i create Proxy for Api class instance, and try to intercept. It's need to redirect to login page if user Unauthenticated and get CSRF cookies if needed and repeat request.
When i trying use Api instance (with Proxy):
async function signIn() {
loading.value = true;
const payload: object = {
email: login.value,
password: password.value,
remember: remember.value
}
try {
const response = await api.post('/login', payload);
console.log(response)
} catch (e) {
console.error(e);
}
loading.value = false;
}
it's thrown error:
LoginForm.vue?0a10:29 TypeError: boot_api__WEBPACK_IMPORTED_MODULE_1__.api.post is not a function
at eval (LoginForm.vue?0a10:26:1)
at Generator.next (<anonymous>)
at eval (VM2781 LoginForm.vue:13:71)
at new Promise (<anonymous>)
at __awaiter (VM2781 LoginForm.vue:9:12)
at signIn (LoginForm.vue?0a10:17:1)
at callWithErrorHandling (runtime-core.esm-bundler.js?f781:155:1)
at callWithAsyncErrorHandling (runtime-core.esm-bundler.js?f781:164:1)
at emit$1 (runtime-core.esm-bundler.js?f781:720:1)
at eval (runtime-core.esm-bundler.js?f781:7292:1)
What's a problem? In IDE has no errors, and if not use Proxy all works...
Full code of LoginForm vue component:
<template>
<q-form
@submit="signIn"
class="column q-gutter-y-md login-form-width"
>
<q-input v-model="login"
label="Login"
class="col"
filled
:disable="loading"
/>
<q-input v-model="password"
label="Password"
type="password"
class="col"
filled
:disable="loading"
/>
<q-checkbox v-model="remember"
label="Remember"
class="col"
filled
:disable="loading"
/>
<q-btn label="Login"
type="submit"
unelevated
color="primary"
:loading="loading"
/>
</q-form>
</template>
<script lang="ts">
import {defineComponent, Ref, ref} from 'vue';
import {api} from 'boot/api';
export default defineComponent({
name: 'LoginForm',
setup() {
const login: Ref<string> = ref('');
const password: Ref<string> = ref('');
const remember: Ref<boolean> = ref(true);
const loading: Ref<boolean> = ref(false);
const errors: Ref<object> = ref({
login: [],
password: [],
});
async function signIn() {
loading.value = true;
const payload: object = {
email: login.value,
password: password.value,
remember: remember.value
}
try {
const response = await api.post('/login', payload);
console.log(response)
} catch (e) {
console.error(e);
}
loading.value = false;
}
return {login, password, remember, loading, errors, signIn}
}
})
</script>
<style scoped>
.login-form-width {
width: 100%;
max-width: 350px;
}
</style>
Full code of api (from quasar boot file):
import {boot} from 'quasar/wrappers'
import {AxiosError, AxiosInstance, Method} from 'axios';
import {axiosInstance} from 'boot/axios';
type ApiMethod = 'get' | 'post';
class ApiResponse {
public data: object
public code: number | null
public message: string | null
constructor(data: object = {}, code: number | null = 0, message: string | null = '') {
this.data = data;
this.code = code;
this.message = message;
}
}
class ApiError {
public data: object
public code: number | null
public message: string | null
constructor(data: object = {}, code: number | null = 0, message: string | null = '') {
this.data = data;
this.code = code;
this.message = message;
}
}
interface ApiRequestConfig {
url: string,
method: Method,
headers?: object,
params?: object,
data?: object | string,
}
class Api {
axios: AxiosInstance
constructor(axios: AxiosInstance) {
this.axios = axios;
this.axios.defaults.withCredentials = true;
}
public async get(url: string, params: object = {}): Promise<ApiResponse> {
return this.request({method: 'GET', url, params});
}
public async post(url: string, payload: object = {}): Promise<ApiResponse> {
return this.request({method: 'POST', url, data: payload});
}
private async request(config: ApiRequestConfig): Promise<ApiResponse> {
try {
const response = await this.axios.request(config);
return new ApiResponse(response.data, response.status, response.statusText)
} catch (e) {
const error = e as AxiosError;
if (error.response) {
throw new ApiError(error.response.data, error.response.status, error.response.statusText);
} else {
throw new ApiError({}, 0, '')
}
}
}
}
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$api: Api;
}
}
let api: Api = new Api(axiosInstance);
// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async ({router}) => {
api = new Proxy(api, {
async get(target: Api, prop: ApiMethod) {
if (typeof target[prop] === 'function') {
return async function func(args: unknown[]): Promise<unknown> {
try {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return await target[prop](...args);
} catch (e) {
const error = e as ApiError;
if (error.code === 401 || error.code === 403) {
await router.push('login');
}
if (error.code === 419) {
await target.get('/sanctum/csrf-cookie');
return func(args);
}
}
}
}
}
})
})
export {api};