Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use \"use node\" directive in action files that need Node.js APIs. Cannot write queries or mutations in \"use node\" files.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 65% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 41% | 0% |
When you need Node.js APIs (fetch, crypto, Buffer, etc.) in Convex, you must use actions with the "use node" directive.
Files with "use node" can ONLY contain:
action functionsinternalAction functionsquery or mutation functionsFiles without "use node" can contain:
query functionsmutation functionsinternalQuery and internalMutation functionsUse actions with "use node" when you need:
typescript"use node"; import { action } from "./_generated/server"; import { v } from "convex/values"; export const fetchWeather = action({ args: { city: v.string() }, handler: async (ctx, args) => { // fetch is available because of "use node" const response = await fetch( `https://api.weather.com/weather?city=${args.city}` ); const data = await response.json(); // Store in database via mutation await ctx.runMutation(api.weather.store, { city: args.city, data: data, }); return data; }, });
typescript"use node"; import { action } from "./_generated/server"; import OpenAI from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); export const generateSuggestion = action({ args: { prompt: v.string() }, handler: async (ctx, args) => { const completion = await openai.chat.completions.create({ model: "gpt-4", messages: [{ role: "user", content: args.prompt }], }); return completion.choices[0].message.content; }, });
typescript"use node"; import { action } from "./_generated/server"; import crypto from "crypto"; export const generateSecureToken = action({ handler: async (ctx) => { const token = crypto.randomBytes(32).toString("hex"); await ctx.runMutation(api.tokens.store, { token }); return token; }, });
typescript"use node"; import { action } from "./_generated/server"; import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); export const createPayment = action({ args: { amount: v.number() }, handler: async (ctx, args) => { const paymentIntent = await stripe.paymentIntents.create({ amount: args.amount, currency: "usd", }); return paymentIntent.client_secret; }, });
typescript"use node"; import { action, mutation } from "./_generated/server"; // ❌ ERROR: Cannot have mutations in "use node" file export const create = mutation({ handler: async (ctx, args) => { // This will fail! }, }); export const fetchData = action({ handler: async (ctx) => { const data = await fetch("..."); return data; }, });
convex/tasks.ts (no "use node"):
typescriptimport { query, mutation } from "./_generated/server"; export const list = query({ handler: async (ctx) => { return await ctx.db.query("tasks").collect(); }, }); export const create = mutation({ args: { title: v.string() }, handler: async (ctx, args) => { return await ctx.db.insert("tasks", { title: args.title }); }, });
convex/tasksActions.ts (with "use node"):
typescript"use node"; import { action } from "./_generated/server"; import { api } from "./_generated/api"; export const generateTaskSuggestions = action({ args: { userId: v.id("users") }, handler: async (ctx, args) => { // Fetch from external AI service const response = await fetch("https://ai-service.com/suggest", { method: "POST", body: JSON.stringify({ userId: args.userId }), }); const suggestions = await response.json(); // Store via mutation for (const suggestion of suggestions) { await ctx.runMutation(api.tasks.create, { title: suggestion.title, }); } return suggestions; }, });
Since actions can't directly modify the database in "use node" files, use this pattern:
typescript// convex/externalActions.ts "use node"; import { action } from "./_generated/server"; import { api, internal } from "./_generated/api"; export const syncFromExternalAPI = action({ handler: async (ctx) => { // 1. Fetch from external API (needs Node.js) const response = await fetch("https://api.example.com/data"); const data = await response.json(); // 2. Write to database via mutation await ctx.runMutation(internal.data.storeExternal, { data: data, }); }, }); // convex/data.ts (no "use node") import { internalMutation } from "./_generated/server"; export const storeExternal = internalMutation({ args: { data: v.any() }, handler: async (ctx, args) => { // Now we can write to database await ctx.db.insert("externalData", args.data); }, });
These work in regular queries/mutations without "use node":
typescript// convex/data.ts (no "use node" needed) import { action } from "./_generated/server"; export const fetchData = action({ handler: async (ctx) => { // Convex provides fetch in actions by default const response = await fetch("https://api.example.com/data"); return await response.json(); }, });
However, if you need Node.js-specific features like:
Then you need "use node".
| Need | Use | Directive | Can Write | |------|-----|-----------|-----------| | Database queries | query | No directive | queries only | | Database writes | mutation | No directive | mutations only | | External API | action | "use node" | actions only | | Node.js APIs | action | "use node" | actions only | | Third-party SDKs | action | "use node" | actions only |
Watch for these errors:
typescript"use node"; // ERROR: Cannot export mutations from "use node" files export const create = mutation({ ... });
typescript"use node"; // ERROR: Cannot export queries from "use node" files export const list = query({ ... });
typescript// ERROR: crypto is not available without "use node" import crypto from "crypto"; export const generate = action({ handler: async (ctx) => { const token = crypto.randomBytes(32); // Will fail! }, });
When writing Convex functions:
action with "use node"action with "use node"action with "use node"query (no "use node")mutation (no "use node")"use node"? → Only action exports"use node"Other measured skills in the registry, with their headline benchmark lift.