Astro
ComputeSDK + Astro
Section titled “ComputeSDK + Astro”Use ComputeSDK to execute code in secure sandboxes from your Astro API endpoints.
1. Install Dependencies
Section titled “1. Install Dependencies”npm install computesdk
# Provider packages (install what you need)
npm install @computesdk/e2b # E2B provider
npm install @computesdk/vercel # Vercel provider
npm install @computesdk/daytona # Daytona provider
2. Configure Environment Variables
Section titled “2. Configure Environment Variables”Create a .env
file and add your provider credentials:
# E2B (get from e2b.dev)
E2B_API_KEY=e2b_your_api_key_here
# Vercel
# Method 1: OIDC Token (recommended)
vercel env pull # Downloads VERCEL_OIDC_TOKEN
# Method 2: Traditional
VERCEL_TOKEN=your_vercel_token_here
VERCEL_TEAM_ID=your_team_id_here
VERCEL_PROJECT_ID=your_project_id_here
# Daytona (get from your Daytona instance)
DAYTONA_API_KEY=your_daytona_api_key_here
3. Run Development Server
Section titled “3. Run Development Server”npm run dev
Navigate to http://localhost:4321
Implementation
Section titled “Implementation”API Route with Request Handler
Section titled “API Route with Request Handler”The simplest way to use ComputeSDK in Astro is with the built-in request handler:
// src/pages/api/compute.ts
import type { APIRoute } from 'astro';
import { handleComputeRequest } from 'computesdk';
import { e2b } from '@computesdk/e2b';
export const POST: APIRoute = async ({ request }) => {
const computeRequest = await request.json();
const response = await handleComputeRequest({
request: computeRequest,
provider: e2b({ apiKey: import.meta.env.E2B_API_KEY! })
});
return new Response(
JSON.stringify(response),
{
status: response.success ? 200 : 500,
headers: { 'Content-Type': 'application/json' }
}
);
};
Custom API Route
Section titled “Custom API Route”For more control, create a custom API route:
// src/pages/api/sandbox.ts
import type { APIRoute } from 'astro';
import { compute } from 'computesdk';
import { e2b } from '@computesdk/e2b';
export const POST: APIRoute = async ({ request }) => {
try {
const { code, runtime } = await request.json();
// Set provider
compute.setConfig({
provider: e2b({ apiKey: import.meta.env.E2B_API_KEY! })
});
// Create sandbox and execute code
const sandbox = await compute.sandbox.create({});
const result = await sandbox.runCode(code, runtime);
// Clean up
await compute.sandbox.destroy(sandbox.sandboxId);
return new Response(JSON.stringify({
success: true,
stdout: result.stdout,
stderr: result.stderr,
executionTime: result.executionTime
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error: any) {
return new Response(JSON.stringify({
success: false,
error: error.message || 'Unknown error'
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
};
Frontend Integration
Section titled “Frontend Integration”Call your API from Astro components:
---
// src/pages/playground.astro
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<title>ComputeSDK Playground</title>
</head>
<body>
<div class="container">
<h1>Code Executor</h1>
<textarea
id="code"
rows="10"
cols="80"
placeholder="Enter your code here..."
>print("Hello World!")</textarea>
<div class="controls">
<select id="runtime">
<option value="python">Python</option>
<option value="node">Node.js</option>
</select>
<button id="execute">Execute Code</button>
</div>
<pre id="output" style="display: none;"></pre>
</div>
<script>
const executeButton = document.getElementById('execute');
const codeInput = document.getElementById('code');
const runtimeSelect = document.getElementById('runtime');
const outputPre = document.getElementById('output');
executeButton?.addEventListener('click', async () => {
const code = codeInput?.value;
const runtime = runtimeSelect?.value;
if (!code) return;
executeButton.disabled = true;
executeButton.textContent = 'Executing...';
try {
const response = await fetch('/api/compute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'compute.sandbox.runCode',
code,
runtime
})
});
const data = await response.json();
if (data.success) {
outputPre.textContent = data.result.stdout;
} else {
outputPre.textContent = `Error: ${data.error}`;
}
outputPre.style.display = 'block';
} catch (error) {
outputPre.textContent = `Error: ${error.message}`;
outputPre.style.display = 'block';
} finally {
executeButton.disabled = false;
executeButton.textContent = 'Execute Code';
}
});
</script>
<style>
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
font-family: Arial, sans-serif;
}
textarea {
width: 100%;
font-family: 'Courier New', monospace;
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 1rem;
}
.controls {
margin-bottom: 1rem;
}
select, button {
padding: 0.5rem;
margin-right: 0.5rem;
}
button {
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
#output {
background: #f5f5f5;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
}
</style>
</body>
</html>
With React Component
Section titled “With React Component”// src/components/CodeExecutor.tsx
import { useState } from 'react';
export default function CodeExecutor() {
const [code, setCode] = useState('print("Hello World!")');
const [output, setOutput] = useState('');
const [loading, setLoading] = useState(false);
const [runtime, setRuntime] = useState<'python' | 'node'>('python');
const executeCode = async () => {
setLoading(true);
try {
const response = await fetch('/api/compute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'compute.sandbox.runCode',
code,
runtime
})
});
const data = await response.json();
if (data.success) {
setOutput(data.result.stdout);
} else {
setOutput(`Error: ${data.error}`);
}
} catch (error: any) {
setOutput(`Error: ${error.message}`);
} finally {
setLoading(false);
}
};
return (
<div className="code-executor">
<h2>Code Executor</h2>
<div className="controls">
<select value={runtime} onChange={(e) => setRuntime(e.target.value as 'python' | 'node')}>
<option value="python">Python</option>
<option value="node">Node.js</option>
</select>
</div>
<textarea
value={code}
onChange={(e) => setCode(e.target.value)}
rows={10}
cols={80}
placeholder="Enter your code here..."
/>
<button onClick={executeCode} disabled={loading}>
{loading ? 'Executing...' : 'Execute Code'}
</button>
{output && <pre className="output">{output}</pre>}
</div>
);
}
---
// src/pages/react-playground.astro
import CodeExecutor from '../components/CodeExecutor.tsx';
---
<html lang="en">
<head>
<meta charset="utf-8" />
<title>React Playground</title>
</head>
<body>
<CodeExecutor client:load />
</body>
</html>
Advanced Examples
Section titled “Advanced Examples”Data Analysis API
Section titled “Data Analysis API”// src/pages/api/analyze.ts
import type { APIRoute } from 'astro';
import { compute } from 'computesdk';
import { e2b } from '@computesdk/e2b';
export const POST: APIRoute = async ({ request }) => {
try {
const { csvData } = await request.json();
compute.setConfig({
provider: e2b({ apiKey: import.meta.env.E2B_API_KEY! })
});
const sandbox = await compute.sandbox.create({});
// Save CSV data
await sandbox.filesystem.writeFile('/data/input.csv', csvData);
// Process data
const result = await sandbox.runCode(`
import pandas as pd
import json
# Read and analyze data
df = pd.read_csv('/data/input.csv')
analysis = {
'rows': len(df),
'columns': len(df.columns),
'summary': df.describe().to_dict(),
'missing_values': df.isnull().sum().to_dict()
}
print(json.dumps(analysis, indent=2))
`);
await compute.sandbox.destroy(sandbox.sandboxId);
return new Response(JSON.stringify({
success: true,
analysis: JSON.parse(result.stdout)
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error: any) {
return new Response(JSON.stringify({
success: false,
error: error.message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
};
File Upload Processing
Section titled “File Upload Processing”// src/pages/api/upload.ts
import type { APIRoute } from 'astro';
import { compute } from 'computesdk';
import { e2b } from '@computesdk/e2b';
export const POST: APIRoute = async ({ request }) => {
try {
const formData = await request.formData();
const file = formData.get('file') as File;
if (!file) {
return new Response(JSON.stringify({
success: false,
error: 'No file provided'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
const fileContent = await file.text();
const sandbox = await compute.sandbox.create({
provider: e2b({ apiKey: import.meta.env.E2B_API_KEY! })
});
// Save uploaded file
await sandbox.filesystem.writeFile(`/uploads/${file.name}`, fileContent);
// Process file
const result = await sandbox.runCode(`
import os
# Get file info
file_path = '/uploads/${file.name}'
file_size = os.path.getsize(file_path)
with open(file_path, 'r') as f:
content = f.read()
lines = len(content.split('\\n'))
chars = len(content)
print(f"File: ${file.name}")
print(f"Size: {file_size} bytes")
print(f"Lines: {lines}")
print(f"Characters: {chars}")
`);
await compute.sandbox.destroy(sandbox.sandboxId);
return new Response(JSON.stringify({
success: true,
info: result.stdout
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error: any) {
return new Response(JSON.stringify({
success: false,
error: error.message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
};
Multi-Provider Support
Section titled “Multi-Provider Support”// src/pages/api/multi-provider.ts
import type { APIRoute } from 'astro';
import { compute } from 'computesdk';
import { e2b } from '@computesdk/e2b';
import { vercel } from '@computesdk/vercel';
import { daytona } from '@computesdk/daytona';
export const POST: APIRoute = async ({ request }) => {
try {
const { provider: providerName, code, runtime } = await request.json();
let provider;
switch (providerName) {
case 'e2b':
provider = e2b({ apiKey: import.meta.env.E2B_API_KEY! });
break;
case 'vercel':
provider = vercel({ token: import.meta.env.VERCEL_TOKEN! });
break;
case 'daytona':
provider = daytona({ apiKey: import.meta.env.DAYTONA_API_KEY! });
break;
default:
return new Response(JSON.stringify({
success: false,
error: 'Invalid provider specified'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
const sandbox = await compute.sandbox.create({ provider });
try {
const result = await sandbox.runCode(code, runtime);
return new Response(JSON.stringify({
success: true,
provider: providerName,
result: result.stdout
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} finally {
await compute.sandbox.destroy(sandbox.sandboxId);
}
} catch (error: any) {
return new Response(JSON.stringify({
success: false,
error: error.message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
};
Workflow API
Section titled “Workflow API”// src/pages/api/workflow.ts
import type { APIRoute } from 'astro';
import { compute } from 'computesdk';
import { e2b } from '@computesdk/e2b';
export const POST: APIRoute = async ({ request }) => {
const sandbox = await compute.sandbox.create({
provider: e2b({ apiKey: import.meta.env.E2B_API_KEY! })
});
try {
const steps: any[] = [];
// Step 1: Setup environment
await sandbox.filesystem.mkdir('/workspace');
const setupResult = await sandbox.runCommand('pip', ['install', 'requests', 'beautifulsoup4']);
steps.push({ step: 1, action: 'setup', output: setupResult.stdout });
// Step 2: Fetch data
const fetchResult = await sandbox.runCode(`
import requests
import json
# Simulate data fetch
data = {"message": "Hello World", "timestamp": "2024-01-01"}
# Save to file
with open('/workspace/data.json', 'w') as f:
json.dump(data, f)
print("Data fetched and saved")
`);
steps.push({ step: 2, action: 'fetch', output: fetchResult.stdout });
// Step 3: Process data
const processResult = await sandbox.runCode(`
import json
with open('/workspace/data.json', 'r') as f:
data = json.load(f)
# Process data
result = {
'original': data,
'processed': data['message'].upper(),
'length': len(data['message'])
}
print(json.dumps(result))
`);
steps.push({
step: 3,
action: 'process',
result: JSON.parse(processResult.stdout)
});
return new Response(JSON.stringify({
success: true,
steps
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error: any) {
return new Response(JSON.stringify({
success: false,
error: error.message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
} finally {
await compute.sandbox.destroy(sandbox.sandboxId);
}
};
Best Practices
Section titled “Best Practices”1. Environment Variables
Section titled “1. Environment Variables”// src/utils/env.ts
export const getProvider = () => {
if (import.meta.env.E2B_API_KEY) {
return e2b({ apiKey: import.meta.env.E2B_API_KEY });
}
if (import.meta.env.VERCEL_TOKEN) {
return vercel({
token: import.meta.env.VERCEL_TOKEN,
teamId: import.meta.env.VERCEL_TEAM_ID,
projectId: import.meta.env.VERCEL_PROJECT_ID
});
}
if (import.meta.env.DAYTONA_API_KEY) {
return daytona({ apiKey: import.meta.env.DAYTONA_API_KEY });
}
throw new Error('No compute provider configured');
};
2. Error Handling
Section titled “2. Error Handling”// src/utils/error-handler.ts
export const handleComputeError = (error: any): Response => {
console.error('Sandbox error:', error);
// Don't expose sensitive error details in production
const message = import.meta.env.DEV ? error.message : 'Internal server error';
return new Response(JSON.stringify({
success: false,
error: message
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
};
3. Input Validation
Section titled “3. Input Validation”// src/utils/validation.ts
import { z } from 'zod';
export const executeSchema = z.object({
code: z.string().min(1).max(10000),
runtime: z.enum(['python', 'node']),
timeout: z.number().optional().default(30000)
});
export const validateRequest = async (request: Request) => {
try {
const body = await request.json();
return executeSchema.parse(body);
} catch (error) {
throw new Error('Invalid request data');
}
};
4. Resource Management
Section titled “4. Resource Management”// src/utils/sandbox-manager.ts
export const withSandbox = async <T>(
provider: any,
callback: (sandbox: any) => Promise<T>
): Promise<T> => {
const sandbox = await compute.sandbox.create({ provider });
try {
return await callback(sandbox);
} finally {
await compute.sandbox.destroy(sandbox.sandboxId);
}
};
Configuration
Section titled “Configuration”Astro Config
Section titled “Astro Config”// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [react()],
output: 'server',
adapter: /* your adapter */
});
TypeScript Config
Section titled “TypeScript Config”// tsconfig.json
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"types": ["astro/client"]
}
}
Deployment
Section titled “Deployment”Environment Variables
Section titled “Environment Variables”Set your environment variables in your deployment platform:
# Production environment variables
E2B_API_KEY=your_production_e2b_key
VERCEL_TOKEN=your_production_vercel_token
VERCEL_TEAM_ID=your_team_id
VERCEL_PROJECT_ID=your_project_id
DAYTONA_API_KEY=your_production_daytona_key
Build Configuration
Section titled “Build Configuration”Ensure your build includes all necessary dependencies and environment variables are properly configured for your deployment platform.
Troubleshooting
Section titled “Troubleshooting”Environment variables not loading?
- Check
.env
file exists and has correct format - Restart dev server after changes
- Use
import.meta.env
to access variables in Astro - Ensure variables are not prefixed with
PUBLIC_
if they contain secrets
Sandbox creation fails?
- Verify API keys are correct and have proper format
- Check provider-specific setup requirements
- Monitor rate limits and quotas
Server-side only errors?
- ComputeSDK must run on server-side only (API endpoints)
- Don’t import ComputeSDK in client-side components or scripts
- Use API endpoints to bridge between client and ComputeSDK
Build errors?
- Ensure all ComputeSDK imports are in API routes only
- Check that environment variables are properly typed
- Verify provider packages are correctly installed
Client hydration issues?
- ComputeSDK operations should happen in API endpoints
- Use
client:load
or other client directives appropriately for interactive components - Avoid server-side imports in client-side code