@renderinc/sdk
v0.4.1
Published
Render SDK for TypeScript
Readme
Render Workflow SDK for TypeScript
The official TypeScript SDK for Render Workflows, providing a simple and intuitive API for managing and executing tasks.
Features
- REST API Client: Run, monitor, and manage task runs
- Task Definition SDK: Define and register tasks with decorators
- Server-Sent Events: Real-time streaming of task run events
- Async/Await Support: Modern Promise-based API
- TypeScript First: Full type safety and IntelliSense support
- Retry Logic: Configurable retry behavior for tasks
- Subtask Execution: Execute tasks from within other tasks
Installation
npm install @renderinc/sdkOr with yarn:
yarn add @renderinc/sdkOr with pnpm:
pnpm add @renderinc/sdkOr with Bun:
bun add @renderinc/sdkQuick Start
REST API Client
Use the Render SDK to run tasks and monitor their execution:
import { Render } from '@renderinc/sdk';
// Create a Render SDK instance (uses RENDER_API_KEY from environment)
const render = new Render();
// Run a task and wait for completion
const result = await render.workflows.runTask('my-workflow/my-task', [42, 'hello']);
console.log('Status:', result.status);
console.log('Results:', result.results);
// Or start a task and decide when to await the result
const run = await render.workflows.startTask('my-workflow/my-task', [42, 'hello']);
console.log('Task run ID:', run.taskRunId);
const details = await run.get();
// List recent task runs
const taskRuns = await render.workflows.listTaskRuns({ limit: 10 });Alternatively, you can create a workflows client directly:
import { createWorkflowsClient } from '@renderinc/sdk/workflows';
const client = createWorkflowsClient();
const result = await client.runTask('my-workflow/my-task', [42, 'hello']);Task Definition
Define tasks that can be executed by the workflow system:
import { task, startTaskServer } from '@renderinc/sdk/workflows';
// Simple task
const square = task(
{ name: 'square' },
function square(a: number): number {
return a * a;
}
);
// Async task with subtask execution
task(
{ name: 'addSquares' },
async function addSquares(a: number, b: number): Promise<number> {
const result1 = await square(a);
const result2 = await square(b);
return result1 + result2;
}
);
// Task with custom options
task(
{
name: 'retryableTask',
retry: {
maxRetries: 3,
waitDurationMs: 1000,
backoffScaling: 1.5,
},
timeoutSeconds: 86400, // 24h
plan: 'starter',
},
async function retryableTask(input: string): Promise<string> {
// Task implementation
return input.toUpperCase();
}
);
// The task server starts automatically when running in a workflow environment
// (when RENDER_SDK_SOCKET_PATH is set). No need to call startTaskServer() explicitly.
//
// To disable auto-start, set RENDER_SDK_AUTO_START=false in your environment.API Reference
Render SDK
new Render(options?)
Creates a new Render SDK instance with access to all Render products.
Options:
token?: string- API token (defaults toRENDER_API_KEYenv var)baseUrl?: string- Base URL (defaults tohttps://api.render.com)useLocalDev?: boolean- Use local development modelocalDevUrl?: string- Local development URLownerId?: string- Default owner ID for object storage (falls back toRENDER_WORKSPACE_IDenv var)region?: string- Default region for object storage (falls back toRENDER_REGIONenv var)
Properties:
workflows- WorkflowsClient instance for managing workflow tasksexperimental- ExperimentalClient instance for object storage and other experimental APIs
Example:
import { Render } from '@renderinc/sdk';
const render = new Render({
token: 'your-api-token',
baseUrl: 'https://api.render.com',
});
// Access workflows client
const result = await render.workflows.runTask('my-workflow/task', [42]);Workflows Client API
The workflows client is accessible via render.workflows or can be created directly using createWorkflowsClient:
import { createWorkflowsClient } from '@renderinc/sdk/workflows';
const client = createWorkflowsClient({
token: 'your-api-token',
baseUrl: 'https://api.render.com',
});Workflows Client Methods
render.workflows.runTask(taskIdentifier, inputData, signal?)
Runs a task and waits for completion.
Parameters:
taskIdentifier: string- Task identifier in format "workflow-slug/task-name"inputData: any[]- Input data as array of parameterssignal?: AbortSignal- Optional abort signal for cancellation
Returns: Promise<TaskRunDetails>
Example:
const render = new Render();
const result = await render.workflows.runTask('my-workflow/square', [5]);
console.log('Results:', result.results);render.workflows.startTask(taskIdentifier, inputData, signal?)
Starts a task run and returns a TaskRunResult. Results are not streamed until you call .get() on the returned result. Use this when you need the task run ID, want to defer awaiting, or want fire-and-forget.
Parameters:
taskIdentifier: string- Task identifier in format "workflow-slug/task-name"inputData: any[]- Input data as array of parameterssignal?: AbortSignal- Optional abort signal for cancellation
Returns: Promise<TaskRunResult>
Example:
const render = new Render();
// Start a task and grab its ID
const run = await render.workflows.startTask('my-workflow/square', [5]);
console.log('Task run ID:', run.taskRunId);
// Await the result when you're ready
const result = await run.get();
console.log('Results:', result.results);render.workflows.taskRunEvents(taskRunIds, signal?)
Streams task run events as an async iterable. Yields a TaskRunDetails for each terminal event (completed, failed, or canceled) received on the stream.
Parameters:
taskRunIds: string[]- One or more task run IDs to subscribe tosignal?: AbortSignal- Optional abort signal for cancellation
Returns: AsyncGenerator<TaskRunDetails>
Example:
const render = new Render();
const run1 = await render.workflows.startTask('my-workflow/square', [3]);
const run2 = await render.workflows.startTask('my-workflow/square', [6]);
// The stream stays open until you break or abort.
const pending = new Set([run1.taskRunId, run2.taskRunId]);
for await (const event of render.workflows.taskRunEvents([...pending])) {
console.log('Event:', event.status, event.id, event.results);
pending.delete(event.id);
if (pending.size === 0) break;
}render.workflows.getTaskRun(taskRunId)
Gets task run details by ID.
Parameters:
taskRunId: string- Task run ID
Returns: Promise<TaskRunDetails>
Example:
const render = new Render();
const details = await render.workflows.getTaskRun('task-run-id');render.workflows.cancelTaskRun(taskRunId)
Cancels a running task.
Parameters:
taskRunId: string- Task run ID to cancel
Returns: Promise<void>
Example:
const render = new Render();
const run = await render.workflows.startTask('my-workflow/square', [5]);
await render.workflows.cancelTaskRun(run.taskRunId);render.workflows.listTaskRuns(params)
Lists task runs with optional filters.
Parameters:
params.limit?: number- Maximum number of resultsparams.cursor?: string- Pagination cursorparams.ownerId?: string[]- Filter by owner IDs
Returns: Promise<TaskRun[]>
Example:
const render = new Render();
const taskRuns = await render.workflows.listTaskRuns({ limit: 10 });Task API
task(options, func)
Registers a function as a task.
Parameters:
options: RegisterTaskOptions- Task configurationname: string- Task name (required)retry?: RetryOptions- Optional retry configurationmaxRetries: number- Maximum number of retrieswaitDurationMs: number- Wait duration between retries in millisecondsbackoffScaling?: number- Backoff multiplier (default: 1.5)
timeoutSeconds?: number- Maximum execution time in secondsplan?: string- Resource plan for task execution (e.g.,"starter","standard","pro")
func: TaskFunction- The task function to register
Returns: The registered function with the same signature
Usage:
// Basic usage
const myTask = task(
{ name: 'myTask' },
function myTask(arg: string): string {
return arg.toUpperCase();
}
);
// With retry, timeout, and plan options
task(
{
name: 'retryableTask',
retry: {
maxRetries: 3,
waitDurationMs: 1000,
backoffScaling: 1.5,
},
timeoutSeconds: 300,
plan: 'starter',
},
function retryableTask(arg: string): string {
return arg.toUpperCase();
}
);
// Async task with subtasks
const square = task(
{ name: 'square' },
function square(a: number): number {
return a * a;
}
);
task(
{ name: 'addSquares' },
async function addSquares(a: number, b: number): Promise<number> {
const result1 = await square(a);
const result2 = await square(b);
return result1 + result2;
}
);startTaskServer()
Starts the task server and listens for task execution requests.
Returns: Promise<void>
Example:
await startTaskServer();Types
TaskRunStatus
enum TaskRunStatus {
PENDING = 'pending',
RUNNING = 'running',
COMPLETED = 'completed',
FAILED = 'failed',
CANCELED = 'canceled',
}TaskRun
interface TaskRun {
id: string;
taskId: string;
status: TaskRunStatus;
startedAt?: string;
completedAt?: string;
parentTaskRunId: string;
rootTaskRunId: string;
retries: number;
}TaskRunDetails
interface TaskRunDetails {
id: string;
taskId: string;
status: TaskRunStatus;
results?: any;
error?: string;
startedAt?: string;
completedAt?: string;
}TaskRunResult
class TaskRunResult {
readonly taskRunId: string;
get(): Promise<TaskRunDetails>;
}RegisterTaskOptions
interface RegisterTaskOptions {
name: string;
retry?: {
maxRetries: number;
waitDurationMs: number;
backoffScaling?: number; // default 1.5
};
timeoutSeconds?: number;
plan?: string; // e.g., "starter", "standard", "pro"
}Error Handling
The SDK provides several error classes:
import { Render } from '@renderinc/sdk';
import {
RenderError,
TaskRunError,
ClientError,
ServerError,
AbortError,
} from '@renderinc/sdk';
const render = new Render();
try {
const result = await render.workflows.runTask('my-workflow/task', [42]);
} catch (error) {
if (error instanceof TaskRunError) {
console.error('Task failed:', error.taskRunId, error.message);
} else if (error instanceof ClientError) {
console.error('Client error:', error.statusCode, error.cause);
} else if (error instanceof ServerError) {
console.error('Server error:', error.statusCode, error.cause);
} else if (error instanceof AbortError) {
console.error('Request was aborted');
} else if (error instanceof RenderError) {
console.error('General SDK error:', error.message);
}
}Environment Variables
RENDER_API_KEY- Your Render API key (required)RENDER_WORKSPACE_ID- Default owner ID for object storage (workspace team ID, e.g.tea-xxxxx)RENDER_REGION- Default region for object storage (e.g.oregon,frankfurt)RENDER_USE_LOCAL_DEV- Enable local development mode (true/false)RENDER_LOCAL_DEV_URL- Local development URL (default:http://localhost:8120)RENDER_SDK_MODE- Task execution mode (runorregister)RENDER_SDK_SOCKET_PATH- Unix socket path for task communication
Object Storage
When running on Render, RENDER_WORKSPACE_ID and RENDER_REGION are set automatically. You can also pass them as constructor options:
import { Render } from '@renderinc/sdk';
const render = new Render(); // Uses env vars for auth + object storage defaults
// Upload (no need to pass ownerId/region when env vars are set)
await render.experimental.storage.objects.put({
key: 'path/to/file.png',
data: Buffer.from('binary content'),
contentType: 'image/png',
});
// Download
const obj = await render.experimental.storage.objects.get({ key: 'path/to/file.png' });
// List
const response = await render.experimental.storage.objects.list();Examples
Example 1: Running a Task
import { Render } from '@renderinc/sdk';
const render = new Render();
const result = await render.workflows.runTask('my-workflow/square', [5]);
console.log('Square of 5 is:', result.results[0]); // 25Example 2: Defining Tasks with Subtasks
import { task } from '@renderinc/sdk/workflows';
const square = task(
{ name: 'square' },
function square(a: number): number {
return a * a;
}
);
task(
{ name: 'pythagorean' },
async function pythagorean(a: number, b: number): Promise<number> {
const aSquared = await square(a);
const bSquared = await square(b);
return Math.sqrt(aSquared + bSquared);
}
);Example 3: Error Handling in Tasks
import { task } from '@renderinc/sdk/workflows';
const divide = task(
{ name: 'divide' },
async function divide(a: number, b: number): Promise<number> {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
}
);
task(
{
name: 'safeDivide',
retry: {
maxRetries: 3,
waitDurationMs: 1000,
},
},
async function safeDivide(a: number, b: number): Promise<number> {
try {
return await divide(a, b);
} catch (error) {
console.error('Division failed:', error);
return 0; // Return default value
}
}
);Example 4: Using AbortSignal for Cancellation
import { Render, AbortError } from '@renderinc/sdk';
const render = new Render();
async function runTaskWithCancellation() {
const abortController = new AbortController();
// Cancel the task after 5 seconds
setTimeout(() => abortController.abort(), 5000);
try {
const result = await render.workflows.runTask(
'my-workflow/long-running-task',
[42],
abortController.signal
);
console.log('Task completed:', result.results);
} catch (error) {
if (error instanceof AbortError) {
console.log('Task was cancelled');
} else {
console.error('Task failed:', error);
}
}
}
runTaskWithCancellation();Example 5: Using the Unified Render SDK
import { Render } from '@renderinc/sdk';
// Single entry point for all Render products
const render = new Render({
token: process.env.RENDER_API_KEY,
});
async function workflowExample() {
try {
// Run a workflow task
const result = await render.workflows.runTask('my-workflow/process-data', [
{ userId: 123, data: 'example' },
]);
console.log('Workflow completed:', result.status);
console.log('Results:', result.results);
// List and monitor recent task runs
const recentRuns = await render.workflows.listTaskRuns({ limit: 5 });
console.log(`\nRecent task runs: ${recentRuns.length}`);
for (const run of recentRuns) {
console.log(`- ${run.id}: ${run.status} (${run.taskId})`);
}
} catch (error) {
console.error('Error:', error);
}
}
workflowExample();Development
Building
npm run buildTesting
npm testLinting
npm run lintFormatting
npm run formatProject Structure
typescript/
├── src/
│ ├── render.ts # Main Render SDK class
│ ├── errors.ts # Error classes
│ ├── index.ts # Main exports
│ ├── version.ts # SDK version and user-agent
│ ├── workflows/ # Workflows functionality
│ │ ├── task.ts # task() function
│ │ ├── runner.ts # startTaskServer() and run()
│ │ ├── executor.ts # TaskExecutor
│ │ ├── registry.ts # TaskRegistry
│ │ ├── uds.ts # Unix socket client
│ │ ├── types.ts # Type definitions
│ │ ├── client/ # REST API client
│ │ │ ├── client.ts # WorkflowsClient class
│ │ │ ├── create-client.ts # createWorkflowsClient() factory
│ │ │ ├── task-run-result.ts # TaskRunResult class
│ │ │ ├── sse.ts # SSE event types
│ │ │ ├── types.ts # Client type definitions
│ │ │ └── index.ts # Exports
│ │ └── index.ts # Workflows exports
│ ├── experimental/ # Experimental features
│ │ └── object/ # Object storage API
│ └── utils/ # Shared utilities
├── examples/
│ ├── client/ # Client example
│ │ ├── main.ts
│ │ └── package.json
│ └── task/ # Task example
│ ├── main.ts
│ └── package.json
├── package.json
├── tsconfig.json
└── README.mdLicense
MIT
Support
For issues and questions, please visit:
- GitHub Issues: https://github.com/renderinc/workflow-sdk/issues
- Documentation: https://render.com/docs/workflows
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
