#Usin Sql
1 messages · Page 1 of 1 (latest)
Hi @royal ruin. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Yes, you can call an external SQL service (or any external API) from Convex, but it must be done through an action, not a query or mutation. Here's what you need to know:
How It Works
Convex functions are divided into three types, and only actions can make external API calls (fetch): [Convex Functions]
| Queries | Mutations | Actions | |
|---|---|---|---|
| Database access | Yes | Yes | No |
| External API Calls (fetch) | No | No | Yes |
Best Practice Pattern
The recommended pattern is not to call an action directly from the client, but instead to:
- Trigger a mutation from the client — the mutation writes intent/state to the database and schedules the action.
- The scheduled action calls the external SQL service.
- The action writes results back via a mutation.
- A query on the client reactively picks up the new data. [Scheduler & Actions]
This is more robust because if the mutation fails, nothing is scheduled (transactional safety), and retries can be handled server-side rather than depending on the client. [Why Not Direct Actions]
Example
// mutation schedules the action
export const sendMessage = mutation({
args: { topic: v.string() },
handler: async (ctx, args) => {
await ctx.db.insert("requests", { topic: args.topic });
await ctx.scheduler.runAfter(0, internal.myModule.fetchFromExternalSQL, {
topic: args.topic,
});
},
});
// action calls the external SQL service
export const fetchFromExternalSQL = internalAction({
args: { topic: v.string() },
handler: async (ctx, args) => {
const response = await fetch("https://your-sql-service.example.com/query", {
method: "POST",
body: JSON.stringify({ query: args.topic }),
});
const data = await response.json();
// Write results back via a mutation
await ctx.runMutation(internal.myModule.saveResults, { data });
},
});
[[Actions Docs](<https://docs.convex.dev/functions/actions>)]
## Key Takeaway
Keep actions **as small as possible** — only the part that requires the external call should live in the action. Most of your logic should stay in queries and mutations for scalability and transactional guarantees. [[Best Practices](<https://docs.convex.dev/tutorial/actions#the-scheduler-actions-and-the-sync-engine>)]
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------