Cache Nuxt Get Request with Axios - vue.js

I have the following setup for caching Axios Requests on my Vue2 Nuxt App. Leaning on (this Github Issue). Though, I can still see requests heading out to the API on the Network console tab. How do I make this start caching the GET requests?
AxCache.js
import LRU from "lru-cache"
export default function(_moduleOptions) {
const ONE_HOUR = 1000 * 60 * 60
const axCache = new LRU({ maxAge: ONE_HOUR })
this.nuxt.hook("vue-renderer:ssr:prepareContext", ssrContext => {
ssrContext.$axCache = axCache
})
}
Axios.js
import { cacheAdapterEnhancer } from "axios-extensions"
import LRU from "lru-cache"
const ONE_HOUR = 1000 * 60 * 60
export default function({ $axios, ssrContext }) {
const defaultCache = process.server
? ssrContext.$axCache
: new LRU({ maxAge: ONE_HOUR })
const defaults = $axios.defaults
// https://github.com/kuitos/axios-extensions
defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
enabledByDefault: false,
cacheFlag: "useCache",
defaultCache
})
}
Then my API call is just a simple one to Reddit.
this.$axios.get(https://www.reddit.com/r/funny/hot.json', { useCache: true })
.then((response) => {
this.reddit = response.data.data.children
})

Related

ThrottlerStorageRedisService on integration service

im trying to build an integration test for a module in NestJS and im having a problem with this package.
I created a redis intances with docker on my local but nothing seems to work.
what am i doing wrong ?
import { config } from '#clients-service/common/config';
import {
DEFAULT_THROTTLE_TTL_SECONDS,
DEFAULT_THROTTLE_LIMIT,
} from '#clients-service/common/constants';
import { RedisCacheModule } from '#clients-service/common/providers/redis-cache';
import { INestApplication } from '#nestjs/common';
import { Test, TestingModule } from '#nestjs/testing';
import { ThrottlerModule } from '#nestjs/throttler';
import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis';
const MAX_TIME = 5 * 1000;
describe('[Module] Clients Service', () => {
jest.setTimeout(MAX_TIME);
let app: INestApplication;
beforeAll(async () => {
const test = new ThrottlerStorageRedisService({
host: config.redis.host,
port: config.redis.port,
password: config.redis.password,
});
const module: TestingModule = await Test.createTestingModule({
imports: [
RedisCacheModule,
ThrottlerModule.forRoot({
ttl: DEFAULT_THROTTLE_TTL_SECONDS,
limit: DEFAULT_THROTTLE_LIMIT,
storage: test,
}),
],
}).compile();
app = module.createNestApplication();
await app.init();
});
it('should be defined', () => {
expect(app).toBeDefined();
});
});

How to get rid of CORS error in Nuxt/SSR?

So, I made a simple api request using Nuxt/SSR. I have cities and categories. Each city has a list of categories. To fetch cities I have to use https://api/cities and to get categories I should use https://api/cities?id
store/cities.js
export const state = () => ({
cities: [],
})
export const mutations = {
setCities(state, cities) {
state.cities = cities
},
}
export const actions = {
async fetch({ commit }) {
const cities = await this.$axios.$get(
'https://api/cities'
)
commit('setCities', cities)
},
}
export const getters = {
cities: (s) => s.cities,
}
And it works perfectly with this index.vue:
export default Vue.extend({
async fetch({ store }) {
if (store.getters['cities/cities'].length === 0) {
await store.dispatch('cities/fetch')
}
},
computed: {
cities() {
return this.$store.getters['cities/cities']
}
}
})
But now I want to fetch categories on click (to choose city ID):
store/categories.js
export const state = () => ({
categories: [],
})
export const mutations = {
setCategories(state, categories) {
state.categories = categories
},
}
export const actions = {
async fetch({ commit }, cityId) {
const categories = await this.$axios.$get(
'https://api/cities?id=' + <data that I get on click>
)
commit('setCategories', categories)
},
}
export const getters = {
categories: (s) => s.categories,
}
And index.vue now:
export default Vue.extend({
computed: {
cityCategories() {
return []
},
},
methods: {
// this function I call on click
selectCity(selectedCityId) {
this.$store.dispatch('categories/fetch', selectedCityId)
}
}
})
But after this I get an error on click:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api/cities?id. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://api/cities?id. (Reason: CORS request did not succeed). Status code: (null).
I'm sure the problem isn't in api, because I tried to fetch categories with 1st method without click (just edited id to 1) and it worked. I have the same local domain, so it's not about headers in api.
It didn't work on click because it was a request from browser/localhost, not from nuxt server. So I just installed nuxtjs proxy to send a request on local server first.
CORS is definitely at the API.
Set the Access-Control-Allow-Origin header on response from the back-end.

Use dependency from boot file in other boot file in Quasar (vuejs)

I am trying to use an instance of an object in a boot file where the instance is created in another boot file. The documentation [0] talks about using an object instance from a boot file and it works fine when using the instance in a component. I would like to access the instance in another boot file.
First boot file that creates the instance looks like this:
import { AuthService } from '../authorization/AuthService';
let oidc = null
export default ({ router, store, Vue }) => {
const OIDC = new AuthService();
router.beforeEach((to, from, next) => {
const allowAnonymous = to.matched.some(record => record.meta.allowAnonymous)
if (allowAnonymous) {
next()
} else {
var isAuthenticated = OIDC.isAuthenticated()
if (isAuthenticated) {
next()
} else {
OIDC.signIn()
}
}
})
Vue.prototype.$oidc = OIDC
oidc = OIDC
}
export { oidc }
And I am trying to use the oidc instance in another boot file like this:
import oidc from "boot/oidc-service"
import axios from 'axios'
let axiosInstance = null;
export default ({ app, router, store, Vue }) => {
const AxiosInstance = axios.create({
baseURL: window.env.BASE_URL
})
AxiosInstance.interceptors.request.use(function (config) {
return oidc.getAccessToken().then(token => {
config.headers.Authorization = `Bearer ${token}`
return config
})
}, (error) => {
return Promise.reject(error)
})
Vue.prototype.$axios = AxiosInstance
axiosInstance = AxiosInstance
}
export { axiosInstance }
I import them in the following order:
boot: [
'oidc-service',
'axios',
...
If I export the class instead of the instance, I can instantiate it and code works as expected. I would like for the oidc object to be a singleton however.
How can I use the instance of oidc in my axios setup?
[0] https://quasar.dev/quasar-cli/boot-files#Accessing-data-from-boot-files
Unless I'm missing something... if oidc is not null, return it, otherwise continue with the initialization:
import { AuthService } from '../authorization/AuthService';
let oidc = null
export default ({ router, store, Vue }) => {
if(oidc !== null) return oidc;
// else continue...

nuxt.js - How to cache axios call at server side for all clients

I am using a vue + nuxt.js application, I like to know if it is possible to cache an axios webservice call for all clients. I have to get some currency reference data and this makes not much sense that every client has to call this data.
Can someone provide me some hints or even an example? Thx.
Here is working solution with latest Nuxt 2.11, using locally defined module.
First add a local module to nuxt.config.js
modules: [
"#/modules/axCache",
...
]
Then
// modules/axCache.js
import LRU from "lru-cache"
export default function(_moduleOptions) {
const ONE_HOUR = 1000 * 60 * 60
const axCache = new LRU({ maxAge: ONE_HOUR })
this.nuxt.hook("vue-renderer:ssr:prepareContext", ssrContext => {
ssrContext.$axCache = axCache
})
}
and
// plugins/axios.js
import { cacheAdapterEnhancer } from "axios-extensions"
import LRU from "lru-cache"
const ONE_HOUR = 1000 * 60 * 60
export default function({ $axios, ssrContext }) {
const defaultCache = process.server
? ssrContext.$axCache
: new LRU({ maxAge: ONE_HOUR })
const defaults = $axios.defaults
// https://github.com/kuitos/axios-extensions
defaults.adapter = cacheAdapterEnhancer(defaults.adapter, {
enabledByDefault: false,
cacheFlag: "useCache",
defaultCache
})
}
Note, this works for both server/client sides and can be configured to work only on one side.
solution found on: https://github.com/nuxt-community/axios-module/issues/99
here is the new solution for cache the whole page
even you can cache consistent api like menu if you need
https://www.npmjs.com/package/nuxt-perfect-cache
npm i nuxt-perfect-cache
// nuxt.config.js
modules: [
[
'nuxt-perfect-cache',
{
disable: false,
appendHost: true,
ignoreConnectionErrors:false, //it's better to be true in production
prefix: 'r-',
url: 'redis://127.0.0.1:6379',
getCacheData(route, context) {
if (route !== '/') {
return false
}
return { key: 'my-home-page', expire: 60 * 60 }//1hour
}
}
]
]
then for cache your api response in redis for all clients:
asyncData(ctx) {
return ctx.$cacheFetch({ key: 'myApiKey', expire: 60 * 2 }, () => {
console.log('my callback called*******')
return ctx.$axios.$get('https://jsonplaceholder.typicode.com/todos/1')
})
}

Getting concurrent multiple api calls hits to the backend server in redux saga with apisauce

We are using ignite boilerplate and apisuace for my application. There is a weird problem where some of the POST api's are hitting multiple times to the backend server which chokes the server and makes the server down. Has anyone faced this problem?
Backend server gets more than 500 concurrent requests within a second. And this behavior intermittent.
indexsaga.js
import { takeLatest, takeEvery } from "redux-saga/effects";
import API from "../Services/Api";
import FixtureAPI from "../Services/FixtureApi";
/* ------------- Types ------------- */
import { LoginTypes } from "../Redux/LoginRedux";
/* ------------- Sagas ------------- */
import {
loginWithOther,
} from "./LoginSaga";
/* ------------- API ------------- */
// The API we use is only used from Sagas, so we create it here and pass along
// to the sagas which need it.
const api = DebugConfig.useFixtures ? FixtureAPI : API.create();
/* ------------- Connect Types To Sagas ------------- */
export default function* root() {
yield [// some sagas receive extra parameters in addition to an action
takeLatest(LoginTypes.USER_REQUEST, loginWithOther, api),
];
}
LoginSaga.js
import { call, put, all, fork, select } from "redux-saga/effects";
import LoginActions from "../Redux/LoginRedux";
export function* loginWithOther(api, action) {
const { email, password, navigation } = action;
let payload = {
user_id: email,
password: password,
};
const response = yield call(api.loginWithEmail, payload);
if (response.ok) {
yield put(LoginActions.userSuccess(response));
} else {
yield put(LoginActions.userFailure());
}
} else {
yield put(LoginActions.userFailure());
}
}
Loginredux.js
import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'
/* ------------- Types and Action Creators ------------- */
const { Types, Creators } = createActions({
userRequest: ['email', 'password'],
userSuccess: ['data'],
userFailure: null
})
export const LoginTypes = Types
export default Creators
/* ------------- Initial State ------------- */
export const INITIAL_STATE = Immutable({
fetching: false,
login_error: false,
data:null
})
/* ------------- Reducers ------------- */
export const request = (state, action) => {
return state.merge({ fetching: true, login_error: false })
}
export const success = (state, action) => {
const { data } = action
return state.merge({
fetching: false,
login_error: false,
data: data
})
}
export const userFailure = (state) =>
state.merge({ fetching: false, login_error: true })
/* ------------- Hookup Reducers To Types ------------- */
export const reducer = createReducer(INITIAL_STATE, {
[Types.USER_REQUEST]: request,
[Types.USER_SUCCESS]: success,
[Types.USER_FAILURE]: userFailure
})
Api.js
// a library to wrap and simplify api calls
import apisauce from "apisauce";
const create = (baseURL = Config.API_URL) => {
// ------
// STEP 1
// ------
//
// Create and configure an apisauce-based api object.
//
const api = apisauce.create({
// base URL is read from the "constructor"
baseURL,
// here are some default headers
headers: {
"Cache-Control": "no-cache",
"Transfer-Encoding": "chunked"
// 'Content-Encoding': 'gzip'
},
// 10 second timeout...
timeout: 20000
});
const URL_STRINGS = {
LOGIN_WITH_OTHER_EMAIL: "/api/v1/login/email",
}
const loginWithEmail = obj => {
return api.post(URL_STRINGS.LOGIN_WITH_OTHER_EMAIL, obj);
};
return {
loginWithEmail,
}
};
// let's return back our create method as the default.
export default {
create
};
LoginScreen.js
import React, { Component } from 'react'
import LoginActions from '../Redux/LoginRedux'
class LoginScreen extends Component {
render(){
<View>
<Text>Login</Text>
</View>
}
onLoginClick(){
this.props.loginWithEmail("exampleemail#example.com","123456")
Ï
}
}
const mapStateToProps = (state) => {
return {
}
}
const mapDispatchToProps = (dispatch) => {
return {
loginWithEmail: (email, password) =>
dispatch(LoginActions.userRequest(email, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)