Skip to main content
This guide covers common patterns and workflows for using the Modal TypeScript SDK.

Creating a client

All interactions with Modal start with creating a ModalClient instance:
import { ModalClient } from "modal";

const modal = new ModalClient();
The client provides access to all Modal services through service properties:
  • modal.apps - App management
  • modal.sandboxes - Sandbox creation and management
  • modal.functions - Function calling
  • modal.cls - Class calling
  • modal.images - Image building
  • modal.volumes - Volume management
  • modal.queues - Queue operations
  • modal.secrets - Secret management
  • modal.proxies - Proxy management

Working with sandboxes

Sandboxes are isolated execution environments where you can run arbitrary code.

Create and run a sandbox

import { ModalClient } from "modal";

const modal = new ModalClient();

const app = await modal.apps.fromName("my-app", {
  createIfMissing: true,
});
const image = modal.images.fromRegistry("alpine:3.21");

const sb = await modal.sandboxes.create(app, image, {
  command: ["cat"],
});
console.log("Sandbox:", sb.sandboxId);

await sb.stdin.writeText("Hello from Modal!");
await sb.stdin.close();
console.log("Output:", await sb.stdout.readText());

await sb.terminate();

Execute commands in a sandbox

const sb = await modal.sandboxes.create(app, image);

const process = await sb.exec(["sh", "-c", "echo 'Hello from exec!'"]);
console.log("Command output:", await process.stdout.readText());

await sb.terminate();

Use named sandboxes

Create a sandbox with a name to reference it later:
const sb = await modal.sandboxes.create(app, image, {
  name: "my-persistent-sandbox",
});

// Later, retrieve the sandbox by name
const sbFromName = await modal.sandboxes.fromName(
  "my-app",
  "my-persistent-sandbox"
);

Calling functions

Call Modal Functions that are deployed with the Python SDK.

Basic function call

const echo = await modal.functions.fromName(
  "libmodal-test-support",
  "echo_string",
);

// Call with positional arguments
let result = await echo.remote(["Hello world!"]);
console.log(result); // "Hello world!"

// Call with keyword arguments
result = await echo.remote([], { s: "Hello world!" });
console.log(result); // "Hello world!"

Spawn functions asynchronously

Spawn a function without waiting for the result:
const func = await modal.functions.fromName("my-app", "process_data");

// Spawn returns a FunctionCall that you can poll later
const call = await func.spawn([{ data: "value" }]);
console.log("Function call ID:", call.functionCallId);

// Later, get the result
const result = await call.get();

Working with classes

Call methods on Modal class instances.

Basic class usage

const cls = await modal.cls.fromName("libmodal-test-support", "EchoCls");
const instance = await cls.instance();
const method = instance.method("echo_string");

// Call with positional arguments
let result = await method.remote(["Hello world!"]);
console.log(result);

// Call with keyword arguments
result = await method.remote([], { s: "Hello world!" });
console.log(result);

Override class options

You can override concurrency, batching, and other options:
const cls = await modal.cls.fromName("my-app", "MyClass");

// Override concurrency settings
const clsWithConcurrency = cls.withConcurrency({
  maxConcurrency: 10,
});

// Override batching settings
const clsWithBatching = cls.withBatching({
  maxBatchSize: 100,
  waitMs: 1000,
});

Building images

Create custom images from registries or with Dockerfile commands.

From a registry

const image = modal.images.fromRegistry("python:3.13");

Add Dockerfile commands

const image = modal.images
  .fromRegistry("alpine:3.21")
  .dockerfileCommands([
    "RUN apk add --no-cache curl=$CURL_VERSION",
  ], {
    secrets: [
      await modal.secrets.fromObject({
        CURL_VERSION: "8.12.1-r1",
      }),
    ],
  })
  .dockerfileCommands(["ENV SERVER=ipconfig.me"]);

const sb = await modal.sandboxes.create(app, image, {
  command: ["sh", "-c", "curl -Ls $SERVER"],
});

Working with volumes

Volumes provide persistent storage across sandbox runs.

Create and mount a volume

const volume = await modal.volumes.fromName("my-volume", {
  createIfMissing: true,
});

const sb = await modal.sandboxes.create(app, image, {
  volumes: { "/mnt/data": volume },
});

// Volume is now mounted at /mnt/data in the sandbox

Use ephemeral volumes

Create temporary volumes that exist only for the duration of use:
const volume = await modal.volumes.ephemeral();

const sb = await modal.sandboxes.create(app, image, {
  volumes: { "/tmp/data": volume },
});

Using secrets

Secrets provide secure environment variables to sandboxes.

From Modal secrets

const secret = await modal.secrets.fromName("my-api-keys");

const sb = await modal.sandboxes.create(app, image, {
  secrets: [secret],
});

From object

Create secrets from key-value pairs:
const secret = await modal.secrets.fromObject({
  API_KEY: "your-api-key",
  DATABASE_URL: "postgresql://...",
});

const sb = await modal.sandboxes.create(app, image, {
  secrets: [secret],
});

Working with queues

Queues allow communication between different parts of your application.

Create and use a queue

const queue = await modal.queues.fromName("my-queue", {
  createIfMissing: true,
});

// Put items in the queue
await queue.put("message 1");
await queue.put({ data: "value" });

// Get items from the queue
const item1 = await queue.get();
const item2 = await queue.get();

console.log(item1, item2);

Iterate over queue items

for await (const item of queue.iterate()) {
  console.log("Processing:", item);
  // Process item...
}

Error handling

The SDK provides specific error types for different failure scenarios:
import { 
  ModalClient, 
  RemoteError, 
  FunctionTimeoutError,
  NotFoundError,
  SandboxTimeoutError,
} from "modal";

try {
  const result = await func.remote(["data"]);
} catch (error) {
  if (error instanceof FunctionTimeoutError) {
    console.error("Function timed out");
  } else if (error instanceof RemoteError) {
    console.error("Remote function failed:", error.message);
  } else if (error instanceof NotFoundError) {
    console.error("Resource not found");
  } else {
    throw error;
  }
}

Resource cleanup

Always clean up resources when you’re done:
const modal = new ModalClient();

try {
  const sb = await modal.sandboxes.create(app, image);
  // Use sandbox...
} finally {
  await sb.terminate();
  modal.close();
}

Next steps

Client configuration

Learn about advanced client configuration

API reference

Explore the full API documentation

Build docs developers (and LLMs) love