The ModalClient is the main entry point for interacting with Modal’s cloud infrastructure. This guide covers initialization and configuration options.
Basic initialization
Create a client with default settings:
import { ModalClient } from "modal";
const modal = new ModalClient();
The client will automatically use credentials from:
- Constructor parameters (if provided)
- Environment variables (
MODAL_TOKEN_ID, MODAL_TOKEN_SECRET)
- Configuration file (
~/.modal.toml)
Configuration options
The ModalClient constructor accepts a ModalClientParams object with these options:
Authentication
Provide credentials directly to the client:
const modal = new ModalClient({
tokenId: "ak-YOUR_TOKEN_ID",
tokenSecret: "as-YOUR_TOKEN_SECRET",
});
Credentials in constructor parameters take precedence over environment variables and config files.
Environment
Specify which Modal environment to use:
const modal = new ModalClient({
environment: "staging",
});
Custom endpoint
Connect to a custom Modal API endpoint:
const modal = new ModalClient({
endpoint: "https://custom-api.modal.com:443",
});
Timeouts
Set default timeout for API calls (in milliseconds):
const modal = new ModalClient({
timeoutMs: 30000, // 30 seconds
});
You can also override timeouts on individual calls.
Retries
Configure maximum retry attempts for failed requests:
const modal = new ModalClient({
maxRetries: 5, // Default is 3
});
Logging
The SDK includes built-in logging for debugging and observability.
Log levels
Set the log level to control verbosity:
const modal = new ModalClient({
logLevel: "debug", // "debug" | "info" | "warn" | "error"
});
Available log levels:
debug - Detailed debugging information
info - General informational messages
warn - Warning messages
error - Error messages only
Custom logger
Provide your own logger implementation:
import type { Logger } from "modal";
const customLogger: Logger = {
debug: (...args) => console.log("[DEBUG]", ...args),
info: (...args) => console.log("[INFO]", ...args),
warn: (...args) => console.warn("[WARN]", ...args),
error: (...args) => console.error("[ERROR]", ...args),
};
const modal = new ModalClient({
logger: customLogger,
});
Environment variable logging
You can also set the log level via environment variable:
export MODAL_LOGLEVEL=debug
Custom gRPC middleware
Add custom middleware for telemetry, tracing, and observability.
The Modal gRPC API is not considered a public API and can change without warning. Use custom middleware at your own risk.
Basic middleware example
import { ModalClient } from "modal";
import type { ClientMiddleware } from "nice-grpc";
const loggingMiddleware: ClientMiddleware = async function* (call, options) {
const startTime = Date.now();
console.log(`Starting call to ${call.method.path}`);
try {
const result = yield* call.next(call.request, options);
const duration = Date.now() - startTime;
console.log(`Call completed in ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - startTime;
console.error(`Call failed after ${duration}ms:`, error);
throw error;
}
};
const modal = new ModalClient({
grpcMiddleware: [loggingMiddleware],
});
Advanced telemetry example
Integrate with OpenTelemetry or other observability tools:
import { ModalClient } from "modal";
import type { ClientMiddleware } from "nice-grpc";
import { trace, context } from "@opentelemetry/api";
const tracingMiddleware: ClientMiddleware = async function* (call, options) {
const tracer = trace.getTracer("modal-client");
const span = tracer.startSpan(call.method.path, {
kind: 1, // CLIENT
});
try {
const result = yield* context.with(
trace.setSpan(context.active(), span),
async () => call.next(call.request, options)
);
span.setStatus({ code: 1 }); // OK
return result;
} catch (error) {
span.setStatus({ code: 2, message: String(error) }); // ERROR
span.recordException(error as Error);
throw error;
} finally {
span.end();
}
};
const modal = new ModalClient({
grpcMiddleware: [tracingMiddleware],
});
For a complete telemetry example, see the telemetry example in the Modal repository.
Service properties
The ModalClient provides access to all Modal services:
const modal = new ModalClient();
// Service properties
modal.apps // App management
modal.sandboxes // Sandbox operations
modal.functions // Function calling
modal.functionCalls // Function call management
modal.cls // Class calling
modal.images // Image building
modal.volumes // Volume management
modal.queues // Queue operations
modal.secrets // Secret management
modal.proxies // Proxy management
modal.cloudBucketMounts // Cloud bucket mounts
Client methods
version()
Get the SDK version:
const version = modal.version();
console.log("Modal SDK version:", version); // e.g., "0.7.3-dev.0"
environmentName()
Get the current environment name:
const env = modal.environmentName();
console.log("Environment:", env);
imageBuilderVersion()
Get the image builder version:
const builderVersion = modal.imageBuilderVersion();
console.log("Image builder version:", builderVersion); // e.g., "2024.10"
close()
Close the client and clean up resources:
const modal = new ModalClient();
try {
// Use the client...
const app = await modal.apps.fromName("my-app");
} finally {
modal.close();
}
Complete configuration example
Here’s a fully configured client with all options:
import { ModalClient } from "modal";
import type { ClientMiddleware } from "nice-grpc";
const telemetryMiddleware: ClientMiddleware = async function* (call, options) {
// Custom telemetry logic...
return yield* call.next(call.request, options);
};
const modal = new ModalClient({
// Authentication
tokenId: process.env.CUSTOM_MODAL_ID,
tokenSecret: process.env.CUSTOM_MODAL_SECRET,
// Environment
environment: "production",
// Network settings
endpoint: "https://api.modal.com:443",
timeoutMs: 60000,
maxRetries: 5,
// Logging
logLevel: "info",
// Custom middleware
grpcMiddleware: [telemetryMiddleware],
});
// Use the client
try {
const app = await modal.apps.fromName("my-app", {
createIfMissing: true,
});
const image = modal.images.fromRegistry("python:3.13");
const sb = await modal.sandboxes.create(app, image);
// Work with sandbox...
await sb.terminate();
} finally {
modal.close();
}
Migration from v0.4
If you’re upgrading from v0.4 or earlier, note these breaking changes:
Old API (v0.4)
import { initializeClient, Function_, App } from "modal";
initializeClient({
tokenId: "...",
tokenSecret: "...",
});
const app = await App.lookup("my-app");
const func = await Function_.lookup("my-app", "my-function");
New API (v0.5+)
import { ModalClient } from "modal";
const modal = new ModalClient({
tokenId: "...",
tokenSecret: "...",
});
const app = await modal.apps.fromName("my-app");
const func = await modal.functions.fromName("my-app", "my-function");
See the Migration Guide for a complete list of changes.
Next steps
Basic usage
Learn basic patterns and workflows
API reference
Explore the full API documentation