Documentation Index Fetch the complete documentation index at: https://mintlify.com/rjdellecese/confect/llms.txt
Use this file to discover all available pages before exploring further.
Node actions provide access to the full Node.js runtime, including the filesystem, crypto libraries, and other Node-specific APIs. They’re perfect for tasks that require capabilities beyond the standard Convex runtime.
What are Node Actions?
Node actions are a special type of action that runs in a Node.js environment instead of the standard Convex runtime. This gives you access to:
File System Read and write files using Node’s fs module
Crypto Advanced cryptographic operations
Native Modules Use Node.js native modules and bindings
System APIs Access operating system functionality
Defining Node Actions
Define a node action using FunctionSpec.nodeAction:
import { FunctionSpec } from "@confect/core" ;
import { Schema } from "effect" ;
export const processImageSpec = FunctionSpec . nodeAction ({
args: Schema . Struct ({
imageId: Schema . String ,
format: Schema . Literal ( "jpeg" , "png" , "webp" ),
}),
returns: Schema . Struct ({
storageId: Schema . String ,
width: Schema . Number ,
height: Schema . Number ,
}),
});
Implementing Node Actions
Implement node actions with access to the NodeContext service:
import { FunctionImpl } from "@confect/server" ;
import { FileSystem , Path } from "@effect/platform-node" ;
import { Effect } from "effect" ;
import { api } from "./api" ;
export const processImage = FunctionImpl . make (
api ,
"images" ,
"process" ,
({ imageId , format }) =>
Effect . gen ( function* () {
const fs = yield * FileSystem . FileSystem ;
const path = yield * Path . Path ;
const storage = yield * StorageActionWriter ;
// Download image from storage
const blob = yield * storage . get ( imageId );
const buffer = Buffer . from ( await blob . arrayBuffer ());
// Use Node.js APIs for image processing
// (example with sharp library)
const sharp = require ( "sharp" );
const processed = yield * Effect . tryPromise (() =>
sharp ( buffer )
. resize ( 800 , 600 )
. toFormat ( format )
. toBuffer ()
);
// Upload processed image
const processedBlob = new Blob ([ processed ]);
const storageId = yield * storage . store ( processedBlob );
// Get image metadata
const metadata = yield * Effect . tryPromise (() =>
sharp ( processed ). metadata ()
);
return {
storageId ,
width: metadata . width ! ,
height: metadata . height ! ,
};
}). pipe ( Effect . orDie )
);
Available Services
Node actions have access to all action services plus Node.js capabilities:
FileSystem Read and write files
Terminal Execute shell commands
Process Access process information
Confect Services
StorageActionWriter Upload and download files
QueryRunner Run database queries
MutationRunner Run database mutations
Auth Access authentication
Common Use Cases
File Processing
import { FileSystem } from "@effect/platform-node" ;
import { Effect } from "effect" ;
const processCSV = FunctionImpl . make (
api ,
"data" ,
"importCSV" ,
({ fileId }) =>
Effect . gen ( function* () {
const fs = yield * FileSystem . FileSystem ;
const storage = yield * StorageActionWriter ;
const db = yield * MutationRunner ;
// Download CSV from storage
const blob = yield * storage . get ( fileId );
const text = yield * Effect . promise (() => blob . text ());
// Parse CSV
const csv = require ( "csv-parse/sync" );
const records = csv . parse ( text , {
columns: true ,
skip_empty_lines: true ,
});
// Import records into database
for ( const record of records ) {
yield * db ( refs . internal . records . create , record );
}
return { imported: records . length };
}). pipe ( Effect . orDie )
);
Cryptographic Operations
import { Effect } from "effect" ;
import * as crypto from "crypto" ;
const generateAPIKey = FunctionImpl . make (
api ,
"auth" ,
"generateKey" ,
({ userId }) =>
Effect . gen ( function* () {
const db = yield * DatabaseWriter < typeof databaseSchema >();
// Generate cryptographically secure API key
const apiKey = yield * Effect . sync (() =>
crypto . randomBytes ( 32 ). toString ( "base64url" )
);
// Hash the key for storage
const hashedKey = yield * Effect . sync (() =>
crypto
. createHash ( "sha256" )
. update ( apiKey )
. digest ( "hex" )
);
// Store hashed key
yield * db . table ( "apiKeys" ). insert ({
userId ,
hashedKey ,
createdAt: Date . now (),
});
// Return raw key (only time it's available)
return { apiKey };
}). pipe ( Effect . orDie )
);
External Command Execution
import { Command } from "@effect/platform-node" ;
import { Effect } from "effect" ;
const generatePDF = FunctionImpl . make (
api ,
"documents" ,
"generatePDF" ,
({ html }) =>
Effect . gen ( function* () {
const storage = yield * StorageActionWriter ;
// Use puppeteer or wkhtmltopdf via command
const result = yield * Command . make ( "wkhtmltopdf" , "-" , "-" ). pipe (
Command . stdin ( "inherit" ),
Command . stdout ( "pipe" ),
Command . start ,
Effect . flatMap (( process ) =>
Effect . gen ( function* () {
// Write HTML to stdin
yield * Effect . tryPromise (() => {
process . stdin . write ( html );
process . stdin . end ();
});
// Read PDF from stdout
const chunks : Buffer [] = [];
for await ( const chunk of process . stdout ) {
chunks . push ( chunk );
}
return Buffer . concat ( chunks );
})
)
);
// Upload to storage
const blob = new Blob ([ result ], { type: "application/pdf" });
const storageId = yield * storage . store ( blob );
return { storageId };
}). pipe ( Effect . orDie )
);
Native Database Connections
import { Effect } from "effect" ;
import { Pool } from "pg" ;
const syncFromPostgres = FunctionImpl . make (
api ,
"sync" ,
"importUsers" ,
() =>
Effect . gen ( function* () {
const db = yield * DatabaseWriter < typeof databaseSchema >();
// Connect to external PostgreSQL database
const pool = new Pool ({
host: process . env . PG_HOST ,
database: process . env . PG_DB ,
user: process . env . PG_USER ,
password: process . env . PG_PASSWORD ,
});
const client = yield * Effect . tryPromise (() => pool . connect ());
try {
const result = yield * Effect . tryPromise (() =>
client . query ( "SELECT * FROM users WHERE synced = false" )
);
// Import users into Convex
for ( const row of result . rows ) {
yield * db . table ( "users" ). insert ({
externalId: row . id ,
name: row . name ,
email: row . email ,
importedAt: Date . now (),
});
}
return { imported: result . rows . length };
} finally {
client . release ();
}
}). pipe ( Effect . orDie )
);
File System Operations
Use Effect’s FileSystem service for file operations:
import { FileSystem , Path } from "@effect/platform-node" ;
import { Effect } from "effect" ;
const processDataFiles = FunctionImpl . make (
api ,
"data" ,
"process" ,
({ directory }) =>
Effect . gen ( function* () {
const fs = yield * FileSystem . FileSystem ;
const path = yield * Path . Path ;
// Read directory
const files = yield * fs . readDirectory ( directory );
// Process each file
const results = yield * Effect . forEach (
files . filter ( f => f . endsWith ( ".json" )),
( file ) =>
Effect . gen ( function* () {
const filePath = path . join ( directory , file );
const content = yield * fs . readFileString ( filePath );
const data = JSON . parse ( content );
// Process data...
return data ;
}),
{ concurrency: 5 }
);
return { processed: results . length };
}). pipe ( Effect . orDie )
);
Best Practices
Use for Node-Specific Tasks
Only use node actions when you need Node.js APIs. Standard actions are faster and scale better.
Handle Errors Properly
Wrap Node.js operations in Effect.tryPromise or Effect.sync for proper error handling.
Clean Up Resources
Always close file handles, database connections, and other resources.
Use Effect Services
Prefer Effect’s FileSystem and other services over raw Node.js APIs for better composability.
Node actions have longer cold start times than standard actions. Use them judiciously for operations that truly need Node.js capabilities.
Limitations
Node actions run in an isolated Node.js environment
They have access to the filesystem but not to your local development machine
Network access is allowed but subject to Convex’s security policies
Maximum execution time is 10 minutes (same as regular actions)
Next Steps
Storage Learn about file storage
Actions Back to functions overview
HTTP API Build HTTP endpoints
Scheduling Schedule node actions