On demand ISR - disable cache and render updated content for the first request - caching

Environment information
Operating System:
Platform: linux
Arch: x64
Version: #66~20.04.1-Ubuntu SMP Wed Jan 25 09:41:30 UTC 2023
Binaries:
Node: 14.17.5
npm: 6.14.14
Yarn: N/A
pnpm: N/A
Relevant packages:
next: 13.1.6
eslint-config-next: 13.1.6
react: 18.2.0
react-dom: 18.2.0
Area(s) of Next.js affected
Data fetching (gS(S)P, getInitialProps)
Link to the site that reproduces this issue
https://on-demand-isr-no-cache.vercel.app/
To Reproduce
Navigate from home page to ssg page - static page
Shows Current date (of last build / revalidation)
Navigate back to home page
Navigate to ssr page
Shows current date (latest)
Click on the date
api request is made which revalidates ssg page - ON DEMAND ISR
Navigate back to home page
Navigate to ssg
Still shows Current date(of last build / revalidation) - not current date after revalidation
Describe the Bug
After revalidating the page with ON DEMAND ISR,
When user hovers the mouse on the to ssg page, api request is made to getStaticProps to get data
If the user clicks on the link before the request is completed(usually this is the case) and navigates - he gets the old data(date)
Upon subsequent visits to the page or on reload, content of the static page is updated
Expected Behavior
This results in real time data mismatch.
User should be navigated to ssg page with revalidated data
How to avoid/disable stale data caching in this scenario?
Code reference
api route
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await res.revalidate('/ssg');
res.json({ message: 'SSG Re validated!' })
}
ssg.tsx page
export async function getStaticProps () {
return { props: { data: [{name: new Date().toString()}] } }
}

Related

How to connect nextjs app to graphql studio

I'm trying to figure out how to open the apollo studio for my next js app.
When I run the local host, I get a message that says:
Server started at http://localhost:5555/graphql
When I click that link, I get a page that says:
GET query missing.
I'm trying to find a way to get to the apollo studio explorer.
For others looking (or maybe for myself the next time I forget), the address: http://localhost:5555/graphql gets inserted in the sandbox address bar, that you find at the url: https://studio.apollographql.com/sandbox/explorer. It won't work if you put the local host address in the url bar
I faced the same issue and have managed to solve it by connecting to apollo studio as a deployed graph (not using the sandbox) but running locally.
Firstly I followed this tutorial https://master--apollo-docs-index.netlify.app/docs/tutorial/production/ which does not use NextJS but it does connect a react app to the apollo studio sandbox then by section 5 it connects the deployed graph to apollo studio. Unfortunately section 5 is quite outdated so i will try to fill in the blanks and get you up and running.
After you have set up an account in apollo studio add a new graph (+ New Graph button). Use whatever architecture you like but I tried this using 'supergraph'.
On the next page ('Publish your schema using Federation') I used the 'schema document' tab and pipeline: 'Federation 2 Supergraph'.This generates the 2 of the 3 env keys you need to add to your local env file and feed it into your app. keys as follows:
APOLLO_KEY - this starts 'service:' and ends before the space, it is a single line about 50 characters long.
APOLLO_GRAPH_REF - this can be found at the end of the line below the APOLLO_KEY. it is a single word with a '#' symbol in the middle.
APOLLO_SCHEMA_REPORTING=true - written as shown here.
Do not close the 'Publish your schema using Federation' page/ re-open it if you have closed it as it will indicate that you have successful connected the graph when you run the app locally after the next step.
Start the app locally using the CLI and in the browser request a page that queries the apollo server.
Watch the CLI as the page is served and you should see the comment 'Apollo usage reporting starting!', also the 'Publish your schema using Federation' page should confirm the graph has been connected. Now you can use all the features of the sandbox as well as monitoring etc.
Hope this helps.
The reason why Next.js doesn't allow you to connect to Apollo Studio is because Next.js does not allow CORS by default in api handlers.
Apollo Studio tries to send a request from its own domain and it's blocked by Next.js default setup.
Let's assume you have your graphql/Apollo server in your NextJs app at /api/graphql path. When you navigate to that path (from your local) by using http://localhost:3000/api/graphql it will show you the welcome page and allow you to access Apollo Sandbox.
Once you enter the Apollo Sandbox in the bottom right corner it will display this message:
When you run the diagnose problem on your local you'll see the following message:
$ npx diagnose-endpoint#1.1.0 --endpoint=http://localhost:3000/api/graphql
Diagnosing http://localhost:3000/api/graphql
⚠️ OPTIONS response is missing header 'access-control-allow-methods: POST'
⚠️ POST response missing 'access-control-allow-origin' header.
If using cookie-based authentication, the following headers are required from your endpoint:
access-control-allow-origin: https://studio.apollographql.com
access-control-allow-credentials: true
Otherwise, a wildcard value would work:
access-control-allow-origin: *
(📫 Interested in previewing a local tunnel to bypass CORS requirements? Please let us know at https://docs.google.com/forms/d/e/1FAIpQLScUCi3PdMerraiy6GpD-QiC_9KEKVHr4oDL5Vef5fIvzqqQWg/viewform )
The solution for the problem looks like this:
import type { NextApiRequest, NextApiResponse } from "next";
import Cors from "cors";
import { server } from "../../apollo";
// Initializing the cors middleware
// You can read here: https://github.com/expressjs/cors#configuration-options
const cors = Cors({
methods: ["POST"],
});
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(
req: NextApiRequest,
res: NextApiResponse,
fn: Function
) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export const config = {
api: {
bodyParser: false,
},
};
const startServer = server.start();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Run cors middleware (to allow Apollo Studio access)
await runMiddleware(req, res, cors);
// run apollo server
await startServer;
await server.createHandler({ path: "/api/graphql" })(req, res);
}
It combines using the Apollo server and this CORS example
the import:
import { server } from "../../apollo"
from the example above is the apollo server that looks like this:
import { ApolloServer } from "apollo-server-micro";
import { typeDefs } from "./schema";
import { resolvers } from "./resolvers";
export const server = new ApolloServer({
typeDefs,
resolvers,
});
You can also use alternative options like embedding sandbox into your app but I'm finding the above solution a bit easier for my current needs so hope it helps you as well.

In Cypress, After an UI operation how to check a particular API call is triggered in Network tab and verify the Status as 200

I have a web application to perform the update operation with the help of a Update button in UI mode.
During that time list of APIs are loaded into the Network tab with XHR type as below. I have to verify one of the API put call is triggered and the status is passed.
url.toString() -- https://abcdef.execute-api.ue-east-2.amazonaws.com/stg2
It contains the RequestedURL value. I manually verified that in Network tab for that particular API put call. Then kept that in cypress.json and reading the url to the current class
Base URL for the UI operation: https://abc-stg2.awsdev.fgh.com/
Note: The both url are modified and dummy one, since it is confidential to share. The understanding is API calls are in AWS and the UI urls are in other environment
Try #1
// After the UI operation
cy.intercept('PUT',url.toString()).as('urupdate')
cy.wait('#urupdate',{requestTimeout:20000})
.its('status')
.should('be.eq',200)
Output:
Try #2
cy.intercept('PUT', url.toString(), (req) => {
if (req.body.status == 200) {
cy.log('pass')
}
})
Output:
The log is not getting printed and till the if statement everything is getting passed
How can we verify the particular API is triggered and the status is 200?
I have gone through the intercept as above and other stuffs in Cypress. But that does not get me the solution. Share your suggestions

Cypress truncates the URL after login

I have a simple login test in cypress (5.6.0, Windows 10). The application I am testing is written in PHP/MySQL. When I run the test in Cypress I get a 404 page not found error following the login.
This does not happen manually or with webdriver, just with Cypress.
Here is the script (which you can try)
describe('Admin Login', () => {
Cypress.config('baseUrl','https://www.edgewordstraining.co.uk/webdriver2')
it.only('Successful Login', () => {
cy.visit ('/')
cy.get('div#right-column > h1').contains('Web Testing Index Page V2 Sprint2')
cy.contains('Login').click()
cy.get('body').contains('User is not Logged in').should('exist')
cy.get('#username').clear()
cy.get('#username').type('edgewords')
cy.get('#password').clear()
cy.get('#password').type('edgewords123')
cy.contains('Submit').click()
//error 404 occurs here
//cy.visit ('/sdocs/add_record.php') //Had to add this as Cypress changes the URL from the form submit for some reason!
cy.get('body').contains('User is Logged in').should('exist')
//Logout
cy.contains('Log Out').click()
//Handle the confirmation Alert
cy.on('window:confirm',(str)=>{
expect(str).to.equal('Do You Really want to Exit the Secure site! Press ok to Continue ')
})
cy.get('body').contains('User was Logged in').should('exist')
});
And when I run with Chrome or Firefox, I get this in the log:
enter image description here
and in the Test Runner, the URL has been truncated:
enter image description here
The URL should be https://www.edgewordstraining.co.uk/webdriver2/sdocs/add_record.php,
but cypress changes it to https://www.edgewordstraining.co.uk/sdocs/add_record.php
webdriver2 is just a physical folder on the web server where this app/site is stored.
I see that someone else had a similar issue, but there are no replies on his question. Could anyone help? Thanks
Since the above post, I have now looked at the login function in the application itself.
else {
// document.write(response);
//location.replace("../sdocs/add_record.php");
//Had to change the line below as in Cypress, it is not moving back to the previous directory
//so have made it an absolute url instead
//window.location.href = "../sdocs/add_record.php";
window.location.href = "https://www.edgewordstraining.co.uk/webdriver2/sdocs/add_record.php";
return false;
}
The relative url path above should just go back to the parent directory (webdriver2) which it does if you use the site manually.
However, I have had to fix this as a fully qualified url to make it work in cypress.
So I have a workaround, but why does cypress handle it differently?

OneSignal registration fails after refresh

I am using OneSignal in my Laravel/Vue app. I have included it within <head> as stated in documentation:
<script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" async=""></script>
<script>
var OneSignal = window.OneSignal || [];
OneSignal.push(function() {
OneSignal.init({
appId: "{{ env('ONESIGNAL_APP_ID') }}"
});
OneSignal.showNativePrompt();
});
</script>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/OneSignalSDKWorker.js')
.then(function () {
console.log('Service worker registered');
})
.catch(function (error) {
console.log('Service worker registration failed:', error);
});
} else {
console.log('Service workers are not supported.');
}
</script>
I also have a service worker of my own, so I've followed the documentation here as well.
What is happening after a hard reset is that service worker gets installed and it is all fine, however once I refresh the page I am getting:
OneSignalPageSDKES6.js?v=151102:1 Uncaught (in promise) InvalidStateError: The current environment does not support this operation.
at Function.getServiceWorkerHref (https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151102:1:41510)
at xe. (https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151102:1:144028)
at Generator.next ()
at r (https://cdn.onesignal.com/sdks/OneSignalPageSDKES6.js?v=151102:1:716)
And I have no idea what does that mean? What is "current environment"? Where to start debugging? I've tried putting console logs around it, however it led me nowhere...
You would start debugging by looking at the source code of the library.
In your case your library is the OneSignal SDK for browsers.
Let's do this!!!
We can see that this error is thrown by getServiceWorkerHref function (which is defined here) and the error message is driven by the InvalidStateReason enumeration:
case InvalidStateReason.UnsupportedEnvironment:
super(`The current environment does not support this operation.`);
break;
If you look at the first linked file, you will see the note on getServiceWorkerHref OneSignal developers left for the those who dare venture into their source code:
else if (workerState === ServiceWorkerActiveState.Bypassed) {
/*
if the page is hard refreshed bypassing the cache, no service worker
will control the page.
It doesn't matter if we try to reinstall an existing worker; still no
service worker will control the page after installation.
*/
throw new InvalidStateError(InvalidStateReason.UnsupportedEnvironment);
}
As you can see, the error is raised when the service worker has the "Bypassed" state. What is that, you may ask? Let's look at ServiceWorkerActiveState enumeration below, in the same file:
/**
* A service worker is active but not controlling the page. This can occur if
* the page is hard-refreshed bypassing the cache, which also bypasses service
* workers.
*/
Bypassed = 'Bypassed',
It seems, when the browser "hard-refreshes" the page, it bypasses the service worker and OneSignal can't properly initialize when that happens. Hard-refresh can happen for a number of reasons — here are some of them (to the best of my knowledge):
if you click the refresh button a bunch of times (usually seconds consecutive refresh within a short period of time may trigger this)
if you have caching disabled in your DevTools
if the server sets a no-cache header
What is happening after a hard reset
I don't know exactly what you mean by "hard reset", but that sounds like it would trigger this issue. I would suggest you close your browser and then visit the page you are working on without using "reset" functions — theoretically, the service worker should be used for caching on consecutive visits and that would ensure OneSignal can function.

Optimizely object is not valid. Failing isFeatureEnabled

I'm trying to use the React and JavaScript SDKs for Optimizely, but getting the following error in the console:
OPTIMIZELY: Optimizely object is not valid. Failing isFeatureEnabled.
More info about my setup below:
Installed via Yarn: yarn add #optimizely/react-sdk
Import statement in the app container:
import {
createInstance
} from '#optimizely/react-sdk'
Logic in render function:
const optimizely = createInstance({
sdkKey: '<SDK_KEY>',
})
const enabled = optimizely.isFeatureEnabled('example_feature', 'user123');
I get this error in the Chrome console:
OPTIMIZELY: Optimizely object is not valid. Failing isFeatureEnabled.
The Optimizely object will log that error when you call isFeatureEnabled before the SDK has successfully loaded your project's datafile. This can happen for a number of reasons outlined below. Looking at the code example provided in the question, it looks like reason #4 is the most likely cause of the error, but here are all of them:
1. Bad SDK key
If you pass in a bad SDK Key to createInstance, the SDK will not successfully load the datafile and you will get this error.
const optimizely = createInstance({
sdkKey: 'invalid-sdk-key'
})
2. Malformed datafile
If you are passing in the datafile directly to createInstance, but pass in an object that isn't the proper datafile format, you will get this error:
const optimizely = createInstance({
datafile: { wrong: 'format' }
})
3. Inaccessible datafile
Make sure you can access the url of your datafile in a web browser: https://cdn.optimizely.com/datafiles/<Your_SDK_Key>.json. If you get an AccessDenied (403) or Not Found (404) error and your account is new, make sure you create something in the Optimizely UI so that Optimizely is triggered to create and upload a proper datafile.
If in the console of your running application you see a 403 or 404 for the request to the datafile, ensure there are no ad-blockers, firewalls, or proxies preventing the SDK from requesting the datafile on Optimizely's CDN from the SDK.
4. Not waiting for Optimizely SDK to be ready
Even if you have the right SDK Key and the SDK can access Optimizely's CDN. If you don't give the SDK enough time for the datafile request to finish, you will be trying to use the SDK before it's ready.
In the JavaScript SDK, this can be solved by using the onReady method:
const optimizely = createInstance({
sdkKey: 'valid-sdk-key',
});
optimizely.onReady().then(() => {
// optimizely is ready to use, with datafile downloaded from the Optimizely CDN
});
If using the <OptimizelyFeature> component of the React SDK, then the <OptimizelyFeature> component will automatically wait until the <OptimizelyProvider> has successfully loaded the datafile before evaluating isFeatureEnabled.
My Firefox console error was
[OPTIMIZELY] - ERROR <timestamp> OPTIMIZELY: Optimizely object is not valid. Failing isFeatureEnabled.
One of my network failures gave me a big clue. The GET request for cdn.optimizely.com showed "Blocked by AdBlocker Ultimate" under the Transferred column.
Solution
I turned off my ad blocker for this site.

Resources