Issue
I'm building multiple Nextjs apps for different subdomains of the same site. We have a REST API backend which has an app-info
endpoint that gives me some important info about SEO and some more stuff. So I have to render these data on server side. The thing is these data won't change often (they are updated by an admin if needed) so There is no need to use getServerSideProps
on every page or App.getInitialProps
. I just need to call this endpoint every hour and create/update a json file based on these data. How can I achieve such behavior? By the way I am not deploying these sites using Vercel
and we use our own servers.
I also tried this tutorial but it didn't work properly. It did run the worker.js
file successfully but I couldn't open website within browser. After adding webpack
part in next.config.js
the website stopped working.
Note: I need to have access to env variables so I can't use this solution. And since we have multiple subdomains and every one of them has a test and production server, it will be so hard for me to use cron-job.org or similar solutions. I want to be able d to have the cron job alongside Next js and run it using a single command (npm start
for example to run both the job and Next js server)
Solution
After playing around, I came up with a script which might not look ideal but this is the closest I could get to what I tried to achieve:
// cron-build.js
const { exec, spawn } = require("child_process")
const fs = require("fs-extra")
require("dotenv").config({
path: "./.env.production.local"
})
const isEqual = require("lodash.isequal")
const cron = require("node-cron")
const execPromise = (command) =>
new Promise((resolve) => {
exec(command, (err, stdout, stderr) => {
resolve({ err, stdout, stderr })
})
})
/**
* fetchs app info and then starts build procedure if appInfo.json does not exist or it differs from server.
*/
const build = async () => {
try {
console.log("fetching app info\n")
const appInfo = await fetchAppInfo()
let currentAppInfo = undefined
try {
currentAppInfo = await fs.readJSON("./src/appInfo.json")
} catch (e) {
}
if (isEqual(appInfo, currentAppInfo)) {
console.log("No change found in app info\n")
rest(true)
return
}
fs.writeJson("./src/appInfo.json", appInfo)
} catch (e) {
console.log(e)
throw e
}
console.log("Change detected in app info. Rebuilding application...\n")
const buildProcess = spawn("npm", ["run", "build"])
buildProcess.addListener("error", (data) => console.log("error", data))
buildProcess.on("exit", (exitCode) => {
if (exitCode === 0) {
console.log("Successful build. Restarting server...\n")
rest()
} else {
console.error(
"Build failed. Run `npm run build` in your terminal to see the logs"
)
}
})
buildProcess.on("message", (data) => console.log("message", data))
buildProcess.stdout.on("data", (data) => console.log(`${data}`))
}
/**
* The rest of the build process (killing current running server and restarting server)
* @param {boolean} noRestart If set to true, only starts the server and won't kill already running server
* @returns
*/
const rest = async (noRestart) => {
const { err: err2, stdout } = await execPromise(
`sudo ss -lptn 'sport = :${process.env.SERVER_PORT || 8080}'`
)
if (err2) {
console.log(err2)
return
}
const pid = stdout
.toString()
.match(/pid=\d+/)?.[0]
?.split("=")?.[1]
if (pid) {
if (noRestart) return
const { err } = execPromise(`sudo kill ${pid}`)
if (err) {
console.log("failed to kill current running server. error:", err)
return
}
}
const server = spawn("npx", ["next", "start", "-p", process.env.SERVER_PORT || 8080])
server.stdout.on("data", (data) => console.log(`${data}`))
server.stdout.on("error", (data) => console.log(`${data}`))
server.on("close", () => server.removeAllListeners())
}
build()
cron.schedule("0 0 * * * *", build)
This script fetches the app-info
from backend and will rebuild the project if data differs from current data using exec
and spawn
(in fact I run next scripts manually and from Node js
). Run this script using sudo node cron-build.js
and you can simply import the output json file inside all your components and pages since this data is available during build time and thus will be compiled inside the project.
Answered By - Ardalan Answer Checked By - Dawn Plyler (WPSolving Volunteer)