Apollo client cache with SSR - next.js

I am using #apollo/client with Next.js Server Side Rendering. The data I am fetching is from wp-graphql, and therefore it is crucial to have them rendered on server side. There is just one slight issue, the cache is forever. I either have to reset the cache every x minutes:
setInterval(async () => {
await client.resetStore();
}, 60_000);
or specify the no-cache policy
const client = new ApolloClient({
uri: "https://the.url",
cache: new InMemoryCache({}),
// For no cache, which is slow
defaultOptions: {
watchQuery: {
fetchPolicy: "no-cache",
errorPolicy: "ignore",
},
query: {
fetchPolicy: "no-cache",
errorPolicy: "all",
},
},
});
Is there any better way to do this, while still using SSR?

Related

NextJS Actioncable Proxy

So I'm trying to do two things at the same time and it's not going too well.
I have a NextJS app and a Rails API server this app connects to. For authentication I'm using a JWT token stored in an http-only encrypted cookie that the Rails API sets and the front end should not be touching. Naturally that creates a necessity for the frontend to send all the api requests though the NextJs server which proxies them to the real API.
To do that I have set up a next-http-proxy-middleware in my /pages/api/[...path] in the following way:
export const config = { api: { bodyParser: false, externalResolver: true } }
export default function handler(
req: NextApiRequest,
res: NextApiResponse
) {
httpProxyMiddleware(req, res, {
target: process.env.BACKEND_URL,
pathRewrite: [{ patternStr: "^/?api", replaceStr: "" }],
})
}
Which works great and life would be just great, but turns out I need to do the same thing with ActionCable subscriptions. Not to worry, found some handy tutorials, packed #rails/actioncable into my package list and off we go.
import {useCurrentUser} from "../../../data";
import {useEffect, useState} from "react";
const UserSocket = () => {
const { user } = useCurrentUser()
const [roomSocket, setRoomSocket] = useState<any>(null)
const loadConsumer = async () => {
// #ts-ignore
const { createConsumer } = await import("#rails/actioncable")
const newCable = createConsumer('/api/wsp')
console.log('Cable loaded')
setRoomSocket(newCable.subscriptions.create({
channel: 'RoomsChannel'
},{
connected: () => { console.log('Room Connected') },
received: (data: any) => { console.log(data) },
}))
return newCable
}
useEffect(() => {
if (typeof window !== 'undefined' && user?.id) {
console.log('Cable loading')
loadConsumer().then(() => {
console.log('Cable connected')
})
}
return () => { roomSocket?.disconnect() }
}, [typeof window, user?.id])
return <></>
}
export default UserSocket
Now when I go to load the page with that component, I get the log output all the way to Cable connected however I don't see the Room Connected part.
I tried looking at the requests made and for some reason I see 2 requests made to wsp. First is directed at the Rails backend (which means the proxy worked) but it lacks the Cookie headers and thus gets disconnected like this:
{
"type": "disconnect",
"reason": "unauthorized",
"reconnect": false
}
The second request is just shown as ws://localhost:5000/api/wsp (which is my NextJS dev server) with provisional headers and it just hangs up in pending. So neither actually connect properly to the websocket. But if I just replace the /api/wsp parameter with the actual hardcoded API address (ws://localhost:3000/wsp) it all works at once (that however would not work in production since those will be different domains).
Can anyone help me here? I might be missing something dead obvious but can't figure it out.

Apollo Client can't query in getServerSideProps but works on client [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
In our Nextjs app we are connecting to a different domain for Apollo Server, queries work fine on the client with cors options and everything. However same queries fails with network fetch error inside getServerSideProps.
I have tried changing headers on the createApolloClient and using fetch with isomorphic-unfetch however there is no response from server side calls to the graphql server.
Our apollo client code is same as the nextjs example on here except the httpLink part we use it like the following
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql',
fetchOptions: {
mode: 'cors',
},
credentials: 'include',
fetch: enhancedFetch,
});
and this is the server configutaion
const server = new ApolloServer({
schema,
validationRules: Meteor.isDevelopment ? [depthLimit(10)] : [NoIntrospection, depthLimit(10)],
context: async ({ req }) => ({
user: await getUser(req.headers.authorization),
}),
cache: 'bounded',
uploads: false,
plugins: Meteor.isDevelopment ? [ApolloServerPluginLandingPageGraphQLPlayground({})] : [],
csrfPrevention: true, // Enable CSRF prevention
});
async function startApolloServer() {
await server.start();
server.applyMiddleware({
app: WebApp.connectHandlers,
path: 'http://localhost:3000/graphql',
cors: {
origin: 'http://localhost:4000',
credentials: true,
},
});
}
When NextJS sends a query form getServerSideProps it receives an error as the following
{
"name": "ApolloError",
"graphQLErrors": [],
"clientErrors": [],
"networkError": {
"cause": {
"errno": -61,
"code": "ECONNREFUSED",
"syscall": "connect",
"address": "::1",
"port": 3000
}
},
"message": "fetch failed"
}

Nextjs and workbox integration

Requirement: I am trying to use service worker and cache static files so as to have a benefit to reduce HTTP requests and make the site performance better. 
Down the lane I would switch to offline, caching images, api's etc.
I have seen the plugins:
https://github.com/hanford/next-offline and
https://www.npmjs.com/package/next-pwa
It seems to work. Although I was trying to find out if there were examples of (nextjs + workbox).
Next js do have an example for https://github.com/vercel/next.js/tree/canary/examples/with-next-offline. But I would like just using workbox for this.
Anyone got any working examples? Even a basic one would do.
Currently am not using a custom server. Just using the inbuilt builder of nextjs (https://nextjs.org/docs/getting-started#manual-setup)
I figured out an answer on my own:
Reference: https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW
I have done runtime caching for my app here and added the workbox file into the base file:
// Use the window load event to keep the page load performant
useEffect(() => {
window.addEventListener("load", () => {
const serviceWorkerScope = `/${country}/workbox-worker.js`
navigator.serviceWorker
.register(serviceWorkerScope)
.then(() => {
logger.info(`Service worker registered at ${serviceWorkerScope}`)
})
.catch(error => {
logger.error("Error in serviceWorker registration: ", error)
})
})
})
I have added comments,
// File to generate the service worker.
require("dotenv").config()
const workboxBuild = require("workbox-build")
const { COUNTRY: country, NODE_ENV } = process.env
const urlPattern = new RegExp(`/${country}\/static|_next\/.*/`)
// https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.generateSW
const buildSW = () => {
return workboxBuild.generateSW({
swDest: "public/workbox-worker.js",
clientsClaim: true,
mode: NODE_ENV,
skipWaiting: true,
sourcemap: false,
runtimeCaching: [
{
urlPattern: urlPattern,
// Apply a cache-first strategy.
handler: "CacheFirst",
options: {
cacheName: "Static files caching",
expiration: {
maxEntries: 50,
maxAgeSeconds: 15 * 60, // 15minutes
},
},
},
],
})
}
buildSW()

Proxy webpack-dev-server from the DOM

I want to add a fetch call to the initial load of index.html on a local app. This data is otherwise loaded server-side, but for this particular case I need to make an http call in dev.
I am having difficulty proxying the webpack-dev-server with the fetch added to the DOM.
The proxy is working correctly after my app is instantiated and I use Axios to make http calls to /api, but on init, the proxy is still serving from localhost instead of the target endpoint.
This is a bit puzzling to me - why might the proxy work in JS post-load but not on init?
devServer: {
contentBase: '/some/path',
port: 9000,
https: true,
open: true,
compress: true,
hot: true,
proxy: { '/api/*': [Object] }
},
Script in index
<script>
(async function() {
const data = await getData();
addDataset(data);
async function getData() {
return fetch('/api/my/endpoint').then(
(response) => response.data.result,
);
}
function addDataset(data) {
var el = document.getElementById('root');
const parsed = JSON.parse(data);
Object.entries(parsed).forEach((entry) => {
const [k, val] = entry;
el.dataset[k] = JSON.stringify(val);
});
}
})();
</script>
Error
400 Bad request
Request URL - https://localhost:9000/api/my/endpoint

Express cookie parser is not creating cookies in production

I am using graphql-yoga and I am writing cookies that access the Microsoft Graph API. On the front end I have an apollo client with NextJS set up and it is working perfectly... in development. When I deploy the server there is no recognition of the cookies from the front end at all. In my reading I think this has something to do with NextJS being server rendered (even though when I run next build it says static build...) I am certain the problem is somewhere in here (I am leaving the comments in, to show all of the places I tried to set the credentials to 'include')
export default function createApolloClient(initialState, ctx) {
// The `ctx` (NextPageContext) will only be present on the server.
// use it to extract auth headers (ctx.req) or similar.
return new ApolloClient({
ssrMode: Boolean(ctx),
link: new HttpLink({
fetch,
uri: process.env.NODE_ENV === 'development' ? endpoint : prodEndpoint,
credentials: 'include', // Additional fetch() options like `credentials` or `headers`
fetchOptions: {
credentials: 'include',
},
// request: operation => {
// operation.setContext({
// fetchOptions: {
// credentials: 'include',
// },
// });
// },
}),
connectToDevTools: true,
// credentials: 'include',
cache: new InMemoryCache().restore(initialState),
});
}
The other answers to this all involved CORS, but I have CORS set up on my my GraphQL-Server:
const opts = {
debug: process.env.NODE_ENV === 'development',
cors:
process.env.NODE_ENV === 'development'
? {
credentials: true,
origin: ['http://localhost:3000'],
}
: {
credentials: true,
origin: [
'...'
],
},
};
server.start(opts, () =>
console.log('Playground is running on http://localhost:4000'),
);
Can anyone point me in the right direction? Am I right to be looking at the ApolloClient portion of my front end? Thanks in advance.
This was staring me in the face, but they warnings were being drowned out in the console. Cookies need to be set with in Chrome.
{
...,
sameSite: false,
secure: true
}
The console had these links to provide insight:
https://www.chromestatus.com/feature/5088147346030592
https://www.chromestatus.com/feature/5633521622188032
This is a very recent change in Chrome, and I only realized that there was a difference because I randomly opened my site in Firefox, and it worked.

Resources