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
})
I'm coming from express, never using next-auth before but unsure how to store user session in a redis database.
On express, I would have done the following;
import express from 'express';
import session from 'express-session';
import connectRedis from 'connect-redis';
import Redis from 'ioredis';
import { __prod__, COOKIE_NAME } from './constants';
const main = async () => {
const RedisStore = connectRedis(session);
const redis = new Redis(process.env.REDIS_URL);
app.use(
session({
name: 'qid',
store: new RedisStore({
client: redis,
disableTouch: true,
ttl: 1000 * 60 * 60 * 24 * 365, // 1 year
}),
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 365, // 1 year
httpOnly: true,
sameSite: 'lax',
secure: __prod__,
domain: __prod__ ? process.env.DOMAIN : undefined,
},
saveUninitialized: false,
secret: process.env.SESSION_SECRET,
resave: false,
}),
);
};
main()
[...nextauth].ts
import NextAuth, { type NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "#next-auth/prisma-adapter";
import { prisma } from "../../../server/db/client";
export const authOptions: NextAuthOptions = {
callbacks: {
session({ session, user }) {
if (session.user) {
session.user.id = user.id;
}
return session;
},
},
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
async authorize(credentials, req) {
//
},
}),
],
};
export default NextAuth(authOptions);
I can't find any implementations of redis in NextAuth, other than using Upstash for caching, but not for sessions.
I made an adapter for Next Auth that uses ioredis to store data in the Hash data structure.
In the Upstash adapter, they store the data with JSON.stringify.
In my adapter, I use the Hash data structure so it is easier to extend the User object.
You can take a look at this repository.
I have a basic Serverless Express app in a lambda, with a route set to async true. I want to trigger this route asynchronously from a different application, and expect it to run in the background without having to wait for the response.
My full serverless.yml
service: service-name
useDotenv: true
custom:
serverless-offline:
useChildProcesses: true
webpack:
webpackConfig: ./webpack.config.js
packager: "yarn"
includeModules:
forceExclude:
- aws-sdk
prune:
automatic: true
includeLayers: true
number: 3
envStage:
staging: staging
domainPrefix:
staging: service.staging
customDomain:
domainName: ${self:custom.domainPrefix.${opt:stage}}.mydomain.com
basePath: ""
stage: ${self:custom.envStage.${opt:stage}}
createRoute53Record: true
plugins:
- serverless-domain-manager
- serverless-webpack
- serverless-prune-plugin
- serverless-offline
provider:
lambdaHashingVersion: "20201221"
name: aws
runtime: nodejs14.x
region: us-east-1
apiGateway:
minimumCompressionSize: 1024
iamRoleStatements:
- Effect: Allow
Action: ssm:Get*
Resource: "arn:aws:ssm:*:*:parameter/myparams/*"
- Effect: Allow
Action: kms:Decrypt
Resource: "*"
functions:
express:
handler: src/index.middyHandler
events:
- http:
path: /
method: options
- http:
path: /{any+} # Catch all routes
method: options
- http:
path: foo/{any+}
method: get
- http:
path: foo/{any+}
method: post
async: true
Note: The role that deploys this app has permissions to read write to Cloudwatch, and I can see logs from the synchronous invocations, but not from async invocations.
My index.middyHandler
import serverless from "serverless-http";
import express from "express";
import helmet from "helmet";
import bodyParser from "body-parser";
import cookieParser from "cookie-parser";
import middy from "#middy/core";
import ssm from "#middy/ssm";
import doNotWaitForEmptyEventLoop from "#middy/do-not-wait-for-empty-event-loop";
import cors from "cors";
import fooRoutes from "./routes/foo";
const app = express();
app.use(
cors({
methods: "GET,HEAD,OPTIONS,POST",
preflightContinue: false,
credentials: true,
origin: true,
optionsSuccessStatus: 204,
})
);
app.use(helmet({ contentSecurityPolicy: false, crossOriginEmbedderPolicy: false }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.get("/ping", (req, res) => {
res.send("Pong!");
});
// Register routes
app.use("/foo", fooRoutes);
const handler = serverless(app);
export const middyHandler = middy(handler)
.use(
doNotWaitForEmptyEventLoop({
runOnError: true,
runOnAfter: true,
runOnBefore: true,
})
)
.use(
ssm({
setToEnv: true,
fetchData: {
MY_KEYS: "ssm/path"
},
})
)
When I call this method, it correctly returns a 200 response immediately. But the actual code is never run, I have a DB insert in there, and it doesn't happen. In the API Gateway I can see the X-Amz-Invocation-Type header is correctly being passed as Event type.
It is not a proxy integration, as required for async invocation
What am I missing here? The route controller is a test and the code is very simple
testAsync: async (req, res) => {
console.log("In Test Async"); // Does not display in Cloudwatch
try {
const { value } = req.body;
const resp = await updateTest(value); // This just inserts an entry in the DB with value
return res.send(resp);
} catch (err) {
return res.status(500).send(err);
}
},
Is there any other setting I'm missing here? I'm not an AWS expert, so any help would be highly appreciated. Thanks!
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')
})
}
I developing nuxt.js app. And point is login & logout.
We will develop a login to the JWT system.
You must remain logged in at vuex.
However, when I refresh the page, vuex is initialized.
I've read git vuex-persistedstate , but it's hard to understand just how to initialize and set it.
What is the best way to develop a login system in nuxt.js?
Thanks.
Using vuex-persisted state would be the best use case for your scenario.
I will walk you through the process of using vuex-persisted state.
Open command line, cd to your project directory, then enter npm install --save vuex-persistedstate. This will install vuex-persistedstate into your project dependencoes.
Now in your store.js file or wherever your defined your vuex store, add the vuex-persistedstate plugin
import createPersistedState from "vuex-persistedstate";
import * as Cookie from "js-cookie";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
user: {
name: "john doe",
age: " 16",
},
loggedIn: false,
hobbies: ["eating", "partying"],
},
plugins: [
createPersistedState({
paths: ["user", "loggedIn"],
getState: (key) => Cookie.getJSON(key),
setState: (key, state) =>
Cookie.set(key, state, { expires: 1, secure: false }),
}),
],
});
You also need js-cookie package which makes handling cookies easier. Use npm install --save js-cookie.
The paths property says which parts of the state to persist, in our case save as cookies.If no path property is given, then the whole state is persisted
From the above example we have mentioned the paths paths: ['user', 'loggedIn'], so only user and loggedIn properties of the state are saved in cookies not hobbies.
In case you are using modules in your store, the way of defining the pats to persist would be as follows:
import createPersistedState from "vuex-persistedstate";
import * as Cookie from "js-cookie";
import myModule from "./myModule";
import myAnotherModule from "./myAnotherModule";
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
user: {
name: "john doe",
age: " 16",
},
loggedIn: false,
hobbies: ["eating", "partying"],
},
modules: {
myModule,
myAnotherModule,
},
plugins: [
createPersistedState({
paths: ["user", "loggedIn", "myModule.<nameOfThePropretyInState>"],
getState: (key) => Cookie.getJSON(key),
setState: (key, state) =>
Cookie.set(key, state, { expires: 1, secure: false }),
}),
],
});
In your paths you will refer to the module's property in the state you want to persist. In the above example, the property of the state that you mention of myModule is persisted. myAnotherModule state is not saved since it is not mentioned in the paths.
That's it . If you want to customize the way you use vuex-persisted state and js-cookie, have a look at their documentation.
If you want to check whether your desired state is saved in cookies then you can console log your cookies like this: console.log(document.cookie in your App.vue created() lifecycle hook
I have used vuex-persist package instead, very easy to get it up and running. This works for SSR too.
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersist from 'vuex-persist'
import actions from './actions'
import mutations from './mutations'
import getters from './getters'
Vue.use(Vuex)
let vuexLocalStorage = null;
if (process.browser) {
vuexLocalStorage = new VuexPersist({
key: 'vuex', // The key to store the state on in the storage provider.
storage: window.localStorage, // or window.sessionStorage or localForage
})
}
export function createStore() {
return new Vuex.Store({
state: {
},
actions,
mutations,
getters,
plugins: process.browser ? [vuexLocalStorage.plugin] : []
})
}
Just make sure to condition everything to just run in the browser
Better to use cookies for saving authorization token, look at this nuxt module
https://github.com/microcipcip/cookie-universal/tree/master/packages/cookie-universal-nuxt
Here sample on vuex store module to set cookie
//call async ajax request to get UUID
const uuidReq = await dispatch('getUUID')
if (uuidReq.hasOwnProperty('meta')) {
commit('setState', {
uuid: uuidReq.meta.links.me.meta.id,
isLogin: true
})
// calculate expires
const expDate = new Date()
expDate.setTime(expDate.getTime() + (state.accExpKey - 0.3) * 1000)
const expDate2 = new Date()
expDate2.setTime(expDate.getTime() + 2592000 * 1000)
const options = {
path: '/',
expires: expDate
}
const options2 = {
path: '/',
expires: expDate2
}
const cookieList = [{
name: 'g_isLogin',
value: true,
opts: options2
},
{
name: 'g_accKey',
value: state.accKey,
opts: options
},
{
name: 'g_refKey',
value: state.refKey,
opts: options2
},
{
name: 'g_userUUID',
value: uuidReq.meta.links.me.meta.id,
opts: options
}
]
this.$cookies.setAll(cookieList)
}
Here sample implementation on custom Nuxt middleware check existing cookie then inject them into vuex state
export default function({ store, route, redirect, app }) {
const isLogin = app.$cookies.get('g_isLogin') === 'true'
const accKey = app.$cookies.get('g_accKey') || ''
const refKey = app.$cookies.get('g_refKey') || ''
const userUUID = app.$cookies.get('g_userUUID') || ''
// console.warn('authenticated isLogin:', isLogin)
// If the user authenticated
if (isLogin) {
store.commit('user/setState', {
isLogin: isLogin,
accKey: accKey,
refKey: refKey,
uuid: userUUID
})
} else {
return redirect('/?prevURL=' + route.path)
}
}
I would strongly recommend using cookies over localStorage with nuxt and the vuex store. Using a package such as univeral-cookie and the built-in nuxtServerInit action, you can populate both client and server stores by reading the cookies on the initial request from the server. You may be limited in the amount of data you can store with cookies but if you implement a RESTful-like API and store ids in your cookies whenever possible, you can server-side fetch that data to populate the full stack store thereby setting yourself up very well in cases where the user refreshes the page. I found it very handy with auth tokens, too, which expire on their own cookie-related behavior and hence wont exist in the store (or its mutation handled decoded data) in cases where the page refreshes.
for using vuex-persistedstate in nuxt both client and server , follow these steps.
For example consider you have a Vuex Module user and you want to persist it . even if you refresh or route to another page.
const user = {
namespaced: true,
state: () => ({
name: 'geeekfa'
}),
mutations: {
name(state, name) {
state.name = name;
},
},
getters: {
name: (state) => {
return state.name;
},
}
}
export default user
install vuex-persistedstate
npm install --save vuex-persistedstate
install cookie & js-cookie
npm install --save cookie js-cookie
after that your package.json is like :
"dependencies": {
...
"cookie": "^0.3.1",
"js-cookie": "^2.2.1",
"vuex-persistedstate": "^4.0.0-beta.3",
...
}
create persistedState.js in ~/plugin/persistedState.js
// persistedState.js
import createPersistedState from 'vuex-persistedstate'
import * as Cookies from 'js-cookie'
import cookie from 'cookie'
export default ({ store, req }) => {
createPersistedState({
paths: ['user'], // your vuex module name
storage: {
getItem: (key) => {
if (process.server) {
const parsedCookies = cookie.parse(req.headers.cookie)
return parsedCookies[key]
} else {
return Cookies.get(key)
}
},
setItem: (key, value) =>
Cookies.set(key, value, { expires: 365, secure: false }),
removeItem: key => Cookies.remove(key)
}
})(store)
}
add this plugin to nuxt.config.js
plugins: [
...
{ src: '~/plugins/persistedState.js' }
...
],
this is enough ! you can persist user module even after refresh in both client and server side . there is no need to change ~/store/index.js file