Hello Folks,
I have query , I am making an App in remix and Graphql .Can anyone tell how we can implement Cron Job in Remix (react library )and Graphql in shopify app
Topic summary
Implementing scheduled tasks (cron) in a Shopify app built with Remix and GraphQL.
-
Core guidance: Cron scheduling isn’t tied to Remix or GraphQL; it depends on the server environment. In Node.js, use node-cron to run periodic tasks and invoke Shopify APIs (e.g., GraphQL queries/mutations). Real apps must handle API auth and errors.
-
Placement questions: Where to put node-cron in a Remix app (server entry, route loader/action, or separate service) and whether node-cron needs system cron. The implication is node-cron runs in Node (no host cron required) as long as the Node process stays up.
-
Latest update (code example): A Remix route (app/routes/app.cron.($id).jsx) demonstrates integrating node-cron via a createCron() function called from loader/action. It:
• Names a task (deferReadOrders-task), avoids duplicates with cron.getTasks().
• Schedules based on ENV (prod: daily 01:30; dev: every minute at :30).
• Supports start/stop via route params and secures actions with OWNER_KEY and admin authentication.
• Executes deferReadOrders with days [14,7,3] and returns JSON status. -
Status: Practical in-app approach provided; no explicit confirmation of resolution. The code snippet is central to understanding the solution.
Hi Mohit12,
You can implement a Cron Job in your app, but it’s not something that is directly related to Remix or GraphQL. It more so depends on the environment where your app is running. However, if you’re looking to schedule some tasks, you could do it server-side, where your app is running. Here’s a general way of how you can set up a cron job in Node.js environment:
-
Install the node-cron package:
npm install --save node-cron -
Import and use it in your Node.js script:
const cron = require('node-cron');
cron.schedule('* * * * *', () => {
console.log('Running a task every minute');
});
In the context of Shopify, you might want to run a task that fetches or updates some data via the Shopify API. In this case, you can use GraphQL queries/mutations within these tasks.
For example:
const cron = require('node-cron');
const { request } = require('graphql-request')
cron.schedule('* * * * *', () => {
const query = `{
shop {
name
primaryDomain {
url
host
}
}
}`
request('https://your-shopify-store.myshopify.com/api/graphql', query)
.then(data => console.log(data))
.catch(err => console.log(err))
});
This will fetch the name and primary domain of your shop every minute. Please note that this is a simplified example. In a real-world scenario, you’d need to handle API authentication and error handling as well.
Hope this helps,
Hi @Liam
Thanks for this information.
In relation to Remix, where would we add the:
const cron = require(‘node-cron’);
Would this go in the Shopify.server.js or entry.server.jsx or perhaps it could be used in the loader method of a route?
Or would I set up a separate mini application with a server.js file, that would make an api call, inside the cron.schedule(), to one of my remix application’s routes, where I have some business logic that I wish to schedule.
I must say I would prefer to keep everything inside my Remix app.
I cannot see any reason, why I can’t import the node-cron library to a route and use it inside the loader or action method. Both of these are server side methods.
Plus when node-cron is imported, does this require cron to be available on the host? Or does NodeJs just emulate what cron does? I mean a NodeJs server is running 24/7, so it should have some sort of scheduling capability?
Hey @Charles_Roberts , did you figure out where to place the cronjob? I am not sure either. Thanks!
Here is an excerpt from:
app/routes/app.cron.($id).jsx
let job = {};
export async function loader({ request, params }) {
const { admin, session } = await authenticate.admin(request);
const { shop } = session;
if('id' in params){
const action = params.id;
createCron(action, shop, process);
}
return json({
ownerKey: '',
action: '',
error: ''
});
}
export async function action({ request, params }) {
const { admin, session } = await authenticate.admin(request);
const { shop } = session;
const formData$ = await request.formData();
const formData = Object.fromEntries(formData$);
/** @type {any} */
const data = {
...formData
};
const errors = validateCronForm(data);
if (errors) {
return json({ errors }, { status: 422 });
}
const action = formData.action;
let cronResponse = {};
let error = '';
if(formData.ownerKey === process.env.OWNER_KEY){
cronResponse = createCron(action, shop, process);
if('error' in cronResponse){
// @ts-ignore
error = 'Task threw an error';
}
}
else{
error = 'Owner key did not match';
}
return json({
responseData: {
ownerKey: formData.ownerKey,
action: formData.action === 'start' ? 'started' : 'stopped',
error
},
errors
});
}
function createCron(action, shop, process){
const cron = require('node-cron');
const cronTime = process.env.ENVIRONMENT === 'prod' ? '30 1 * * * ' : '30 * * * * * ';
let error = false;
const tasks = cron.getTasks();
let deferReadOrdersTaskExists = false;
for (let [key, value] of tasks.entries()) {
if(value.options.name === 'deferReadOrders-task'){
deferReadOrdersTaskExists = true;
break;
}
}
if(!deferReadOrdersTaskExists){
job = cron.schedule(cronTime, async function execute() {
const date = new Date().toLocaleString();
const days = [14,7,3];
const res = deferReadOrders(days, shop, process);
},{
name: 'deferReadOrders-task',
scheduled: false
});
}
try{
action == 'stop' ? job.stop() : job.start();
}
catch(error){
job.stop();
error = true;
}
return json({
cronTime,
action,
error
});
}