besimking
besimking2mo ago

Cron Jobs, Actions, Internal Mutation

I am trying to call gemini api from my backend and I want to record response to the db. After that I want to make a cron job to call this action. I also want world peace.
11 Replies
Convex Bot
Convex Bot2mo ago
Thanks for posting in <#1088161997662724167>. Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets. - Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.) - Use search.convex.dev to search Docs, Stack, and Discord all at once. - Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI. - Avoid tagging staff unless specifically instructed. Thank you!
erquhart
erquhart2mo ago
You would use an action to first get the api response, and then use ctx.runMutation to record to the db.
besimking
besimkingOP2mo ago
Just to be clear Create a Cron Job Define a cron job that will execute at regular intervals. The cron job will trigger an internal mutation. Call an Internal Mutation The cron job calls an internal mutation where the main logic is handled. Invoke the Action Inside the internal mutation, call an action that sends a request to the Gemini API. The action is responsible for fetching the necessary data. Process the Response Take the response from the action in the internal mutation, validate it, and ensure it's in the correct format. Insert into the Database in Internal Mutation Save the processed data into your database using the mutation. Is that correct?
erquhart
erquhart2mo ago
Is this something you want to just schedule a cron for, or do you want to schedule the cron starting whenever the gemini call is made Prompted by a user action or just generally scheduled In either case, you'll want your primary function to be an action, not a mutation. The action can call the api, and then run the mutation to insert the api response, all in the same action. A mutation can schedule an action to run after the mutation completes, an action can run a mutation directly.
besimking
besimkingOP2mo ago
Generally Scheduled Let's say that I want to send a requst to gemini api every 5 minutes and I will directly record it to the database. I will create blog posts automatically. No direct or indirect user interaction is included for this process BTW if it was a need to make a schedule according to user interaction we have to use ctx.scheduler.runAfter. Is that so?
erquhart
erquhart2mo ago
Right, you'd schedule it But what you're describing is a basic cron: - Write a mutation that accepts gemini data via args - Write an action that calls the gemini api and then calls the mutation with the args - Schedule a cron to run the action every 5 minutes - boom, world peace
besimking
besimkingOP2mo ago
"use node";

import { GoogleGenerativeAI } from "@google/generative-ai";
import { action } from "./_generated/server"
import { api } from "./_generated/api";
import { useMutation } from "convex/react";


const genAi = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);

const model = genAi.getGenerativeModel({
model: "gemini-1.5-flash",
generationConfig: {
temperature: 0.7,
},
});

const targetAudience = "Restaurant and Cafe Owners";
const language = "English";
const prompt = `
Generate a JSON list containing 5 elements. Each element should be an object with the following structure:
.
.
.
`;

export const getTopics = action({
args: {},
handler: async (ctx) => {
const result = await model.generateContent(prompt);
const cleanData: Topic[] = JSON.parse(result.response.text().replace(/`|json/g, '').trim());
for (const data of cleanData) {
const mutate = useMutation(api.geminiMutation.writeTopics)
mutate({ data })
}
},
});

type Topic = {
primaryKeyword: string;
secondaryKeywords: string[];
}
"use node";

import { GoogleGenerativeAI } from "@google/generative-ai";
import { action } from "./_generated/server"
import { api } from "./_generated/api";
import { useMutation } from "convex/react";


const genAi = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);

const model = genAi.getGenerativeModel({
model: "gemini-1.5-flash",
generationConfig: {
temperature: 0.7,
},
});

const targetAudience = "Restaurant and Cafe Owners";
const language = "English";
const prompt = `
Generate a JSON list containing 5 elements. Each element should be an object with the following structure:
.
.
.
`;

export const getTopics = action({
args: {},
handler: async (ctx) => {
const result = await model.generateContent(prompt);
const cleanData: Topic[] = JSON.parse(result.response.text().replace(/`|json/g, '').trim());
for (const data of cleanData) {
const mutate = useMutation(api.geminiMutation.writeTopics)
mutate({ data })
}
},
});

type Topic = {
primaryKeyword: string;
secondaryKeywords: string[];
}
import { v } from "convex/values";
import { mutation } from "./_generated/server";
export const writeTopics = mutation({
args: {
data: v.object({
primaryKeyword: v.string(),
secondaryKeywords: v.array(v.string()),
})
},
handler: async (ctx, args) => {
return await ctx.db.insert('topics', args.data);
},
});
import { v } from "convex/values";
import { mutation } from "./_generated/server";
export const writeTopics = mutation({
args: {
data: v.object({
primaryKeyword: v.string(),
secondaryKeywords: v.array(v.string()),
})
},
handler: async (ctx, args) => {
return await ctx.db.insert('topics', args.data);
},
});
import { cronJobs } from "convex/server";
import { api } from "./_generated/api";

const crons = cronJobs();

crons.interval(
"get topics",
{ minutes: 50 },
api.geminiAction.getTopics
);


export default crons;
import { cronJobs } from "convex/server";
import { api } from "./_generated/api";

const crons = cronJobs();

crons.interval(
"get topics",
{ minutes: 50 },
api.geminiAction.getTopics
);


export default crons;
This is the thing and still no world peace
'Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
' +
'1. You might have mismatching versions of React and the renderer (such as React DOM)
' +
'2. You might be breaking the Rules of Hooks
' +
'3. You might have more than one copy of React in the same app
' +
'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'
'Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
' +
'1. You might have mismatching versions of React and the renderer (such as React DOM)
' +
'2. You might be breaking the Rules of Hooks
' +
'3. You might have more than one copy of React in the same app
' +
'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'
erquhart
erquhart2mo ago
That error is from your client app, but the code you shared is all convex code also if this is just run as a cron, there shouldn't be any client side interaction? oohh you're running useMutation in your convex function - you want ctx.runMutation:
export const getTopics = action({
args: {},
handler: async (ctx) => {
const result = await model.generateContent(prompt);
const cleanData: Topic[] = JSON.parse(result.response.text().replace(/`|json/g, '').trim());
for (const data of cleanData) {
await ctx.runMutation(api.geminiMutation.writeTopics, { data })
}
},
});
export const getTopics = action({
args: {},
handler: async (ctx) => {
const result = await model.generateContent(prompt);
const cleanData: Topic[] = JSON.parse(result.response.text().replace(/`|json/g, '').trim());
for (const data of cleanData) {
await ctx.runMutation(api.geminiMutation.writeTopics, { data })
}
},
});
Also you can make that action internal since it's only called by cron, that way it isn't needlessly exposed in your public api
besimking
besimkingOP2mo ago
How exactly? Yeah I've found that out 🤣 I am sorry for this
erquhart
erquhart2mo ago
action() -> internalAction()
besimking
besimkingOP2mo ago
I am trying Problem solved. Princess saved. Thanks Spider-Man World Peace ☮️

Did you find this page helpful?