Skip to main content

Accessing Project Data from Code Nodes

Code nodes can securely access your project's library files and databases, enabling powerful data-driven workflows. Read files, query databases, generate reports, and persist results—all from within your JavaScript or Python code.

What is Code Node Data Access?

Code node data access enables your custom code to interact with Intellectible's built-in storage systems:

  • Library Access - List, download, upload, and manage files in your project library
  • Database Access - Query, insert, update, and delete data in your project databases

This unlocks workflows that combine custom code with persistent data storage, without requiring external databases or file storage services.

Built-In SDK

The LibraryClient and DatabaseClient SDKs are pre-installed in all code execution environments:

JavaScript:

const { LibraryClient, DatabaseClient } = require('@intellectible/execution-sdk');

const library = new LibraryClient();
const database = new DatabaseClient();

Python:

from intellectible_execution import LibraryClient, DatabaseClient

library = LibraryClient()
database = DatabaseClient()

No installation, no configuration—just import and use.

When to Use Data Access

Library Access Use Cases

Use CaseDescriptionExample
File ProcessingDownload and transform filesConvert PDF to text, resize images
Report GenerationCreate and upload reportsGenerate CSV/PDF from workflow results
Data ImportProcess uploaded filesParse CSV, extract data from documents
File ManagementOrganize library programmaticallyArchive old files, rename/move files
Content AnalysisAnalyze file contentsSentiment analysis, keyword extraction

Database Access Use Cases

Use CaseDescriptionExample
Execution LoggingTrack workflow runsLog timestamps, inputs, results
Data AggregationCollect results over timeBuild metrics dashboard
Cache/LookupStore reusable dataAPI response cache, reference tables
Audit TrailRecord changes and eventsUser actions, system events
State ManagementPersist workflow stateResume interrupted workflows

Common Patterns

Pattern 1: Process Files from Library

Download files, transform them, and upload results back to the library.

JavaScript:

const { LibraryClient } = require('@intellectible/execution-sdk');
const fs = require('fs');

const library = new LibraryClient();

// Read inputs
const inputs = JSON.parse(fs.readFileSync('inputs.json', 'utf8'));

// List all CSV files
const files = await library.listFiles();
const csvFiles = files.filter(f => f.type === 'text/csv');

console.log(`Found ${csvFiles.length} CSV files`);

let result;

// Download and process first CSV
if (csvFiles.length > 0) {
const downloadUrl = await library.createDownloadUrl(csvFiles[0].id);
const response = await fetch(downloadUrl);
const csvData = await response.text();

// Process CSV data (parse, transform, aggregate)
const rows = csvData.split('\n');
const summary = `Processed ${rows.length} rows from ${csvFiles[0].name}`;

// Upload summary as new file (single call handles everything)
await library.uploadFile('summary.txt', 'txt', summary);

result = { processed: csvFiles[0].name, summary };
} else {
result = { message: 'No CSV files found' };
}

// Write output
fs.writeFileSync('output.json', JSON.stringify(result, null, 2));

Python:

import json
import requests
from intellectible_execution import LibraryClient

library = LibraryClient()

# Read inputs
with open('inputs.json', 'r') as f:
inputs = json.load(f)

# List all CSV files
files = library.list_files()
csv_files = [f for f in files if f['type'] == 'text/csv']

print(f'Found {len(csv_files)} CSV files')

# Download and process first CSV
if csv_files:
download_url = library.create_download_url(csv_files[0]['id'])
response = requests.get(download_url)
csv_data = response.text

# Process CSV data
rows = csv_data.split('\n')
summary = f'Processed {len(rows)} rows from {csv_files[0]["name"]}'

# Upload summary (single call handles everything)
library.upload_file('summary.txt', 'txt', summary.encode('utf-8'))

result = {'processed': csv_files[0]['name'], 'summary': summary}
else:
result = {'message': 'No CSV files found'}

# Write output
with open('output.json', 'w') as f:
json.dump(result, f)

Pattern 2: Log Workflow Execution

Create an execution log table and record each workflow run.

JavaScript:

const { DatabaseClient } = require('@intellectible/execution-sdk');
const fs = require('fs');

const database = new DatabaseClient();

// Read inputs
const inputs = JSON.parse(fs.readFileSync('inputs.json', 'utf8'));

// Use an existing database
const databases = await database.listDatabases();
const logDb = databases[0];

// Get or create table
const tables = await database.listTables(logDb.id);

if (!tables.includes('executions')) {
await database.createTable(logDb.id, 'executions');
await database.createColumn(logDb.id, 'executions', 'workflow_name', 'text');
await database.createColumn(logDb.id, 'executions', 'status', 'text');
await database.createColumn(logDb.id, 'executions', 'executed_at', 'text');
await database.createColumn(logDb.id, 'executions', 'duration_ms', 'number');
await database.createColumn(logDb.id, 'executions', 'result', 'jsonb');
}

// Log current execution
const executionStart = Date.now();

// ... perform workflow logic ...
const workflowResult = { success: true, items_processed: inputs.count };

const duration = Date.now() - executionStart;

await database.insertRows(logDb.id, 'executions', [{
workflow_name: inputs.workflowName || 'unnamed',
status: 'success',
executed_at: new Date().toISOString(),
duration_ms: duration,
result: JSON.stringify(workflowResult)
}]);

// Query recent executions
const recent = await database.query(logDb.id, 'executions',
'SELECT * FROM "executions" ORDER BY "executed_at" DESC LIMIT 5'
);

const output = {
logged: true,
recentExecutions: recent.length,
thisExecution: { duration, result: workflowResult }
};

// Write output
fs.writeFileSync('output.json', JSON.stringify(output, null, 2));

Python:

from intellectible_execution import DatabaseClient
from datetime import datetime
import json
import time

database = DatabaseClient()

# Read inputs
with open('inputs.json', 'r') as f:
inputs = json.load(f)

# Use an existing database
databases = database.list_databases()
log_db = databases[0]

# Get or create table
tables = database.list_tables(log_db['id'])

if 'executions' not in tables:
database.create_table(log_db['id'], 'executions')
database.create_column(log_db['id'], 'executions', 'workflow_name', 'text')
database.create_column(log_db['id'], 'executions', 'status', 'text')
database.create_column(log_db['id'], 'executions', 'executed_at', 'text')
database.create_column(log_db['id'], 'executions', 'duration_ms', 'number')
database.create_column(log_db['id'], 'executions', 'result', 'jsonb')

# Log current execution
execution_start = time.time()

# ... perform workflow logic ...
workflow_result = {'success': True, 'items_processed': inputs.get('count', 0)}

duration = int((time.time() - execution_start) * 1000)

database.insert_rows(log_db['id'], 'executions', [{
'workflow_name': inputs.get('workflowName', 'unnamed'),
'status': 'success',
'executed_at': datetime.now().isoformat(),
'duration_ms': duration,
'result': json.dumps(workflow_result)
}])

# Query recent executions
recent = database.query(log_db['id'], 'executions',
'SELECT * FROM "executions" ORDER BY "executed_at" DESC LIMIT 5'
)

output = {
'logged': True,
'recent_executions': len(recent),
'this_execution': {'duration': duration, 'result': workflow_result}
}

# Write output
with open('output.json', 'w') as f:
json.dump(output, f)

Pattern 3: Build Incremental Data Pipeline

Read from library, process data, store results in database.

JavaScript:

const { LibraryClient, DatabaseClient } = require('@intellectible/execution-sdk');
const fs = require('fs');

const library = new LibraryClient();
const database = new DatabaseClient();

// Setup database
const databases = await database.listDatabases();
const pipelineDb = databases[0];
const dbId = pipelineDb.id;

const tables = await database.listTables(dbId);
if (!tables.includes('processed_files')) {
await database.createTable(dbId, 'processed_files');
await database.createColumn(dbId, 'processed_files', 'file_name', 'text', { colIsUnique: true });
await database.createColumn(dbId, 'processed_files', 'processed_at', 'text');
await database.createColumn(dbId, 'processed_files', 'record_count', 'number');
}

// Get already processed files
const processed = await database.query(pipelineDb.id, 'processed_files',
'SELECT * FROM "processed_files"'
);
const processedNames = new Set(processed.map(r => r.file_name));

// Find unprocessed CSV files
const files = await library.listFiles();
const unprocessedCsvs = files.filter(f =>
f.type === 'text/csv' && !processedNames.has(f.name)
);

console.log(`Found ${unprocessedCsvs.length} new files to process`);

// Process each file
for (const file of unprocessedCsvs) {
const downloadUrl = await library.createDownloadUrl(file.id);
const response = await fetch(downloadUrl);
const csvData = await response.text();

const rowCount = csvData.split('\n').length - 1; // Exclude header

// Mark as processed
await database.insertRows(pipelineDb.id, 'processed_files', [{
file_name: file.name,
processed_at: new Date().toISOString(),
record_count: rowCount
}]);

console.log(`Processed ${file.name}: ${rowCount} records`);
}

const output = {
newFiles: unprocessedCsvs.length,
totalProcessed: processed.length + unprocessedCsvs.length
};

// Write output
fs.writeFileSync('output.json', JSON.stringify(output, null, 2));

Pattern 4: Bulk CSV Import to Database

Upload a CSV file to the library, then import it directly into a database table.

JavaScript:

const { LibraryClient, DatabaseClient } = require('@intellectible/execution-sdk');
const fs = require('fs');

const library = new LibraryClient();
const database = new DatabaseClient();

// Generate CSV data
const csvData = [
'name,email,age',
'Alice,alice@example.com,30',
'Bob,bob@example.com,25',
'Charlie,charlie@example.com,35'
].join('\n');

// Upload to library (single call handles everything)
const docId = await library.uploadFile('users.csv', 'csv', csvData);

// Use an existing database and create table
const databases = await database.listDatabases();
const dbId = databases[0].id;
await database.createTable(dbId, 'users');
await database.createColumn(dbId, 'users', 'name', 'text');
await database.createColumn(dbId, 'users', 'email', 'text', {
colIsUnique: true
});
await database.createColumn(dbId, 'users', 'age', 'number');

// Import CSV into table
const importResult = await database.uploadCSV(dbId, 'users', docId);

const output = {
fileId: docId,
rowsImported: importResult.rowsInserted,
errors: importResult.errors
};

// Write output
fs.writeFileSync('output.json', JSON.stringify(output, null, 2));

Security Model

Project Isolation

Code nodes can only access data from their own project:

  • Library files - Only files in the current project's library
  • Databases - Only databases created in the current project
  • No cross-project access - Cannot read or write to other projects

This isolation is enforced by the execution token, which is cryptographically bound to the project ID.

Automatic Authentication

The SDK handles all authentication automatically:

  1. No API keys in code - Token provided by Intellectible infrastructure
  2. No credentials to manage - Authentication is transparent
  3. Token lifecycle - Created when execution starts, destroyed when it ends
  4. Short-lived - 10-minute expiration limits exposure

Network Security

Code execution containers have restricted network access:

  • ✅ Allowed - Project library and database APIs
  • ✅ Allowed - External HTTP/HTTPS APIs

Best Practices

  1. Never log tokens - SDK abstracts token handling
  2. Handle errors gracefully - Database/library operations can fail
  3. Validate data - Don't trust user inputs blindly
  4. Limit query sizes - Use pagination for large result sets
  5. Clean up resources - Delete temporary files/tables when done

Performance Considerations

Library Operations

  • Download time - Large files take longer to download
  • Upload time - Large files take longer to upload
  • Processing time - Factor in time to parse/transform file contents

Tip: For files >10MB, consider streaming or chunked processing.

Database Operations

  • Use LIMIT - Always add a LIMIT clause to SQL queries on large tables
  • Indexes - Use colIsIndexed: true on frequently queried columns
  • Bulk imports - Use insertRows with multiple rows or uploadCSV for bulk imports
  • JSON columns - Avoid very large JSON objects (>1MB)

Execution Timeout

Code nodes have a 10-minute execution timeout. Plan accordingly:

  • Budget time for library downloads (~5-30 seconds for large files)
  • Budget time for database queries (~100ms-5s depending on data size)
  • Leave buffer time for your processing logic

Limitations

Limitations

  • Execution timeout - 10 minutes total (including all SDK operations)
  • No persistent state - Containers are destroyed after execution

Troubleshooting

Common Errors

"Failed to list files: Unauthorized"

  • Cause: Execution token expired or invalid
  • Solution: This should not happen during normal execution; contact support

"Failed to query table: Table not found"

  • Cause: Table doesn't exist or wrong table ID
  • Solution: Use listTables() to verify table names

"Query timeout"

  • Cause: Query taking too long (>30 seconds)
  • Solution: Add pagination, reduce result set size, or add indexes

"File download failed: 404"

  • Cause: File ID doesn't exist or was deleted
  • Solution: Use listFiles() to get current file IDs

Debugging Tips

  1. Log operations - Use console.log to trace SDK calls
  2. Check return values - Verify IDs returned from create operations
  3. Test incrementally - Test library and database access separately
  4. Verify data types - Ensure column types match inserted data
  5. Handle errors - Wrap SDK calls in try-catch blocks

Next Steps

Ready to use code node data access in your workflows?

API Documentation:

Related Guides: