serverless-next.js
serverless-next.js copied to clipboard
Nextjs 12 plugin version for manual regerneration of page ISR
Hello, when will the plugin version for nextjs 12 come out? We would need it for the manual page regeneration feature Thank you very much :)
I will be attempting to work on that, with the help of @Misfits09 and @alexandrepernin! Stay tuned. :hugs:
Thank you very much guys! :)
Any news guys? :) @apollisa
I was able to get manual regeneration running after following issue #2417. The regeneratePage function shown below is run for each page in my /api/revalidate endpoint.
You will need to npm/yarn add/install @sls-next/lambda-at-edge Check your cloudwatch logs and s3 bucket if you run into any trouble. You might need to the getRegenerationOptions filepaths depending on how your project laid out.
import { triggerStaticRegeneration } from '@sls-next/lambda-at-edge/dist/lib/triggerStaticRegeneration'
import S3 from "aws-sdk/clients/s3";
const region = process.env.defaultRegion
const siteSQS = process.env.siteSQS
const websiteBucket = process.env.bucketname
const accessKeyId = process.env.AWS_ACCESS_KEY;
const secretAccessKey = process.env.AWS_SECRET_KEY;
export async function getBuildId(): Promise<string> {
const s3 = new S3({
region,
accessKeyId,
secretAccessKey,
});
const data = function() {
const downloadParams = {
Key: 'BUILD_ID',
Bucket: websiteBucket,
};
return new Promise((res)=>{
s3.getObject(downloadParams, (err,data)=>{
if(err) {
console.log("get BUILD_ID Error: ", err);
} else {
res(data.Body.toString())
}
})
})
}
const file = await data()
// @ts-ignore
return (file || '')
}
export function getStaticPagesPath(buildId: string) {
return `static-pages/${buildId}`
}
export function getRegenerationOptions({
queueName,
buildId,
page,
bucket,
region,
counter
}: {
queueName: string
buildId: string
page: string
bucket: string
region: string
counter: number
}) {
return {
basePath: '',
pagePath: 'pages/' + (page || 'index') + '.js',
pageS3Path: (page ? page : 'index') + '.html',
storeName: bucket,
storeRegion: region,
request: {
uri: (page ? "/" + page : '/'),
origin: {
s3: {
region,
domainName: `${bucket}.s3.${region}.amazonaws.com`,
path: '/' + getStaticPagesPath(buildId)
}
}
},
eTag: Date.now() - 1000 + counter,
lastModified: Date.now() - 1000 + counter,
queueName
}
}
export async function regeneratePage( rawpage: string, counter = 0) {
const buildId = await getBuildId()
const page:string = rawpage.substring(1)
const options = getRegenerationOptions({
queueName: siteSQS,
buildId,
page,
bucket: websiteBucket,
region: region,
counter
})
console.log(
"params:",
{
queueName: siteSQS,
buildId,
page,
bucket: websiteBucket,
region: region,
counter
},
"returnd options",
options)
await triggerStaticRegeneration(options as any)
return true
}
I was able to get manual regeneration running after following issue #2417. The regeneratePage function shown below is run for each page in my /api/revalidate endpoint.
You will need to npm/yarn add/install @sls-next/lambda-at-edge Check your cloudwatch logs and s3 bucket if you run into any trouble. You might need to the getRegenerationOptions filepaths depending on how your project laid out.
import { triggerStaticRegeneration } from '@sls-next/lambda-at-edge/dist/lib/triggerStaticRegeneration' import S3 from "aws-sdk/clients/s3"; const region = process.env.defaultRegion const siteSQS = process.env.siteSQS const websiteBucket = process.env.bucketname const accessKeyId = process.env.AWS_ACCESS_KEY; const secretAccessKey = process.env.AWS_SECRET_KEY; export async function getBuildId(): Promise<string> { const s3 = new S3({ region, accessKeyId, secretAccessKey, }); const data = function() { const downloadParams = { Key: 'BUILD_ID', Bucket: websiteBucket, }; return new Promise((res)=>{ s3.getObject(downloadParams, (err,data)=>{ if(err) { console.log("get BUILD_ID Error: ", err); } else { res(data.Body.toString()) } }) }) } const file = await data() // @ts-ignore return (file || '') } export function getStaticPagesPath(buildId: string) { return `static-pages/${buildId}` } export function getRegenerationOptions({ queueName, buildId, page, bucket, region, counter }: { queueName: string buildId: string page: string bucket: string region: string counter: number }) { return { basePath: '', pagePath: 'pages/' + (page || 'index') + '.js', pageS3Path: (page ? page : 'index') + '.html', storeName: bucket, storeRegion: region, request: { uri: (page ? "/" + page : '/'), origin: { s3: { region, domainName: `${bucket}.s3.${region}.amazonaws.com`, path: '/' + getStaticPagesPath(buildId) } } }, eTag: Date.now() - 1000 + counter, lastModified: Date.now() - 1000 + counter, queueName } } export async function regeneratePage( rawpage: string, counter = 0) { const buildId = await getBuildId() const page:string = rawpage.substring(1) const options = getRegenerationOptions({ queueName: siteSQS, buildId, page, bucket: websiteBucket, region: region, counter }) console.log( "params:", { queueName: siteSQS, buildId, page, bucket: websiteBucket, region: region, counter }, "returnd options", options) await triggerStaticRegeneration(options as any) return true }
Hey @GlennStreetman thanks for sharing the example. Where do I get the params bucketname
, siteSQS
and defaultRegion
? It seems like they are not present in env variables
I was able to get manual regeneration running after following issue #2417. The regeneratePage function shown below is run for each page in my /api/revalidate endpoint. You will need to npm/yarn add/install @sls-next/lambda-at-edge Check your cloudwatch logs and s3 bucket if you run into any trouble. You might need to the getRegenerationOptions filepaths depending on how your project laid out.
import { triggerStaticRegeneration } from '@sls-next/lambda-at-edge/dist/lib/triggerStaticRegeneration' import S3 from "aws-sdk/clients/s3"; const region = process.env.defaultRegion const siteSQS = process.env.siteSQS const websiteBucket = process.env.bucketname const accessKeyId = process.env.AWS_ACCESS_KEY; const secretAccessKey = process.env.AWS_SECRET_KEY; export async function getBuildId(): Promise<string> { const s3 = new S3({ region, accessKeyId, secretAccessKey, }); const data = function() { const downloadParams = { Key: 'BUILD_ID', Bucket: websiteBucket, }; return new Promise((res)=>{ s3.getObject(downloadParams, (err,data)=>{ if(err) { console.log("get BUILD_ID Error: ", err); } else { res(data.Body.toString()) } }) }) } const file = await data() // @ts-ignore return (file || '') } export function getStaticPagesPath(buildId: string) { return `static-pages/${buildId}` } export function getRegenerationOptions({ queueName, buildId, page, bucket, region, counter }: { queueName: string buildId: string page: string bucket: string region: string counter: number }) { return { basePath: '', pagePath: 'pages/' + (page || 'index') + '.js', pageS3Path: (page ? page : 'index') + '.html', storeName: bucket, storeRegion: region, request: { uri: (page ? "/" + page : '/'), origin: { s3: { region, domainName: `${bucket}.s3.${region}.amazonaws.com`, path: '/' + getStaticPagesPath(buildId) } } }, eTag: Date.now() - 1000 + counter, lastModified: Date.now() - 1000 + counter, queueName } } export async function regeneratePage( rawpage: string, counter = 0) { const buildId = await getBuildId() const page:string = rawpage.substring(1) const options = getRegenerationOptions({ queueName: siteSQS, buildId, page, bucket: websiteBucket, region: region, counter }) console.log( "params:", { queueName: siteSQS, buildId, page, bucket: websiteBucket, region: region, counter }, "returnd options", options) await triggerStaticRegeneration(options as any) return true }
Hey @GlennStreetman thanks for sharing the example. Where do I get the params
bucketname
,siteSQS
anddefaultRegion
? It seems like they are not present in env variables
- Bucketname: The s3 bucket for your serverless deployment.
- defaultRegion: The AWS regions, us-east-1 is an example. This should be the region your lambda is deployed to.
- siteSQS: The simple query service queue related to your deployment.
I added each of the above to my .env file. These names didn't exist before my first deploy so i had to deploy serverless, go into the AWS console and update my variables, then update my .env file and redeploy.
Remember that for this to all work you will need to make sure permissions are all setup correctly in IAM for Lambda, S3, and SMS.
@GlennStreetman i think i understand the code, but im confused, how does this work
pagePath: 'pages/' + (page || 'index') + '.js'
On my build the pages are appended unique ids (them seem unique) on the chunks folder (which is the only place with these javascript files)
my homepage js file for example is located at \static\chunks\pages\index-a29c946d7927b337.js
i can deploy this function as a lambda and call it successfully and send the SQS message but the regeneration lambda can't process the messaage because it cannot find the js file
Is it meant to find the js file inside the chunks folder? if so, how does it know which one it is with just the page name?
update: disregard my last comment, sls-next is doing something to find the right js file, my issue was with having dynamically generated pages
but @GlennStreetman what is the usage of the counter? i see it being passed around
@GlennStreetman thanks for your insight here. Would you be able to share a code snippet of how you're calling this within the /api/revalidate endpoint?
@apollisa any update on an official update to the serverless-nextjs project?
@ba221400 The API endpoint shown below is called by Strapi CMS. staticRoutes & revalidateLookup is specific to my small hobby app. Your mapping will definitely be different. The "const update = " is cleanup needed for my particular use case. Hope this helps.
import {regeneratePage} from '../../lib/revalidateLambda'
interface req {
body: reqPayload
headers: any
}
interface reqPayload {
event: string, //entry.update
createdAt: string,
model: string, //faq, holiday, service, team, banner-text, contact, google-map, homepage-intro, our-story, page-title, service-home, banner-image, service-home, site-link, site-text
entry: {[key:string]: any}, //based upon model
headers: any
}
const staticRoutes = ["/", "/about", "/calendar", "/careers", "/contact", "/resumeSubmitted", "/service", "/thankyou" ];
const exampleAPIEndPoint = async (req:req, res) => {
try {
if (req.headers.secret === process.env.NEXT_REVALIDATE) {
const update = req?.body?.model ? req.body.model.replaceAll('-', '') : ''
const serviceName = update === 'service' ? req.body.entry.name : 'false'
console.log('revalidate request received: ', update)
const revalidateLookup = {
faq: staticRoutes,
holiday: ["/calendar"],
holliday: ["/calendar"],
service: function(){
// console.log('revalidating Now', serviceName)
return [`/service/${serviceName.replaceAll(' ', '_')}`, '/service']
}(),
team: ['/about'],
bannertext: staticRoutes,
contact: staticRoutes,
googleMap: [ "/about", "/contact"],
homepageintro: ['/'],
ourstory: ['/', '/about'],
pagetitle: ['/about'],
servicehome: ["/service"],
bannerimage: staticRoutes,
sitelink: staticRoutes,
sitetext: staticRoutes,
}
const revalidateList = revalidateLookup?.[update] || []
for(const el of revalidateList){
console.log('revaliding:', el)
await regeneratePage(`${el}`)
// await res.revalidate(`${el}`);
console.log( el, 'revalid complete')
}
//here
res.status(200).json({update: update})
} else {
console.log('revalidateHook: Missing Secret')
res.status(400).json({ msg: "Error" });
}
} catch (err) {
console.log("/api/revalidateHook Error:", req.body ,err);
res.status(400).json({ msg: "Error" });
}
};
export default exampleAPIEndPoint;