I've just started using NextJs getStaticProps
, and static files generated at build time
is neat. But my contents just don't stay unchanged, I need static files to be updated but rebuilding the app everytime there's a modification is costly. Is there a way to generate new static files only. getServerSideProps
turned out to be taking a big amount of time til first byte.
Depending on type of you content Incremental Statatic Regeneration could be a good solution. But in my experience it introduced other problems when rendering catalog/category pages. As nextjs has no idea if one part of you data is dependent on some other it may render an old catalog page with a link to post or product which no longer exists. This usually happens in combination with a 'fallback' feature for dynamic routes.
It also won't refresh you page right after you made changes so you will see a result only after some time.
Possible workaround is to load posts/product on category page dynamically via ajax. You will lose a part of UX and SEO but on the other hand this solution is relatively easy to maintain.
There is also an option or rather hack to rebuild parts of saved content by directly accessing the cache in custom server. Add 'purge=1' to the page address you want to refreshed.
const { ServerResponse, createServer } = require('http')
const next = require('next')
const { promises } = require('fs')
const path = require('path')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const purgeByPath = async url => {
const pathname = url == '/' ? '/index' : url
const fullPathname = `.next/server/pages${pathname}`
const fullPathHTML = `${fullPathname}.html`
const fullPathJSON = `${fullPathname}.json`
try {
await promises.unlink(fullPathHTML)
await promises.unlink(fullPathJSON)
} catch (err) {
console.log(`Could not unlink cache files: ${err}`)
}
await app.incrementalCache.cache.del(pathname)
let req = new Request(process.env.BASE_HOST + url)
let res = new ServerResponse(req)
await app.renderToHTML(req, res, url, { _nextDataReq: true })
}
app.prepare().then(() => {
createServer(async (req, res) => {
const url = new URL(req.url, "http://localhost:3000/")
if (req.method == 'POST' && req.url == '/purge-cache') {
let raw = ''
req.on('data', chunk => raw += chunk)
req.on('end', async () => {
const data = JSON.parse(raw)
if (!data || !data.urls) {
res.statusCode = 400
res.end()
return
}
for (let url of data.urls) {
console.log(`Cleaning cache on: ${url}`)
await purgeByPath(url)
}
res.end()
})
} else if (url.searchParams.get('purge') == '1') {
await purgeByPath(req.url)
res.end()
} else {
handle(req, res)
}
}).listen(3000, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:3000/`)
})
})
The static cache consists of two parts:
.next/server/pages
where each route will probably have two files html and json. Most likely you'll need to delete both.Cache implementation is not documented an might be swapped or removed in future versions.
This won't work on vercel and has some issues with scaling. You should also add some kind of security token to purge routes.