Timeout
The timeout middleware enforces a maximum request duration, automatically aborting any request that exceeds the configured limit. This prevents slow or hanging requests from consuming resources and helps maintain consistent application responsiveness.
Quick Start
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
// Abort requests that take longer than 30 seconds
app.use(timeout({ ms: 30000 }));
app.get("/process", async (c) => {
// This handler must complete within 30 seconds
const result = await expensiveComputation();
return c.json(result);
});
Deno.serve(app.handler);
How It Works
Aborts requests that exceed the configured duration. Timer starts when middleware executes and includes all downstream middleware and handlers. Returns 408 if timeout exceeded. Properly cleans up timers.
Options
| Option | Type | Default | Description |
|---|---|---|---|
ms |
number |
30000 |
Maximum request duration in milliseconds. Must be a positive number. |
Examples
Default Configuration
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
// Use 30 second default timeout for all requests
app.use(timeout());
app.get("/api/status", (c) => {
return c.json({ status: "ok" });
});
Custom Timeout
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
// Use 5 second timeout for quick endpoints
app.use(timeout({ ms: 5000 }));
app.get("/health-check", (c) => {
return c.text("healthy");
});
Route-Specific Timeouts
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
// Quick endpoints need fast response
app.get("/api/quick", timeout({ ms: 2000 }), (c) => {
return c.json({ data: "fast" });
});
// Long-running operations get more time
app.post("/api/batch-process", timeout({ ms: 120000 }), async (c) => {
const result = await processBatch();
return c.json(result);
});
// Background jobs may need even longer
app.post("/api/export", timeout({ ms: 300000 }), async (c) => {
const csv = await generateLargeExport();
return c.text(csv, "text/csv");
});
Global Timeout with Route Overrides
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
// Global default: 30 seconds
app.use(timeout({ ms: 30000 }));
// Override for specific routes that need different limits
app.get("/api/webhook", timeout({ ms: 5000 }), (c) => {
// Webhooks should respond quickly
return c.json({ received: true });
});
app.post("/api/long-task", timeout({ ms: 120000 }), async (c) => {
// Long tasks get extended timeout
const result = await longRunningOperation();
return c.json(result);
});
Error Handling
When a request exceeds the timeout, the middleware returns a 408 Request Timeout response with an error message. You can handle this using error middleware or a catch-all error handler.
Default Timeout Response
// Client receives:
// Status: 408
// Body: [Timeout middleware] Request exceeded timeout of 30000ms
Custom Error Handling
import { MageApp, MageError } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
// Custom error handling middleware
app.use(async (c, next) => {
try {
await next();
} catch (err) {
if (err instanceof MageError && err.status === 408) {
return c.json({ error: "Request took too long. Please try again." }, 408);
}
throw err;
}
});
app.use(timeout({ ms: 30000 }));
app.get("/api/search", async (c) => {
const results = await searchDatabase();
return c.json(results);
});
Common Patterns
Slow Requests Detection
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
app.use((c, next) => {
const start = Date.now();
return next().finally(() => {
const duration = Date.now() - start;
console.log(`${c.req.method} ${c.req.path} took ${duration}ms`);
});
});
// Apply timeout to catch requests exceeding threshold
app.use(timeout({ ms: 30000 }));
Different Timeouts for Different Routes
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
const quickTimeout = timeout({ ms: 5000 });
const normalTimeout = timeout({ ms: 30000 });
const slowTimeout = timeout({ ms: 120000 });
// API routes - fast response required
app.get("/api/users", quickTimeout, (c) => {
return c.json(users);
});
app.get("/api/posts", quickTimeout, (c) => {
return c.json(posts);
});
// Admin routes - normal timeout
app.post("/admin/update", normalTimeout, async (c) => {
await updateAdmin(c.req.body);
return c.json({ success: true });
});
// Batch operations - extended timeout
app.post("/batch/export", slowTimeout, async (c) => {
const file = await generateExport();
return c.text(file);
});
Timeout with Streaming
import { MageApp } from "@mage/app";
import { timeout } from "@mage/app/timeout";
const app = new MageApp();
app.use(timeout({ ms: 60000 }));
// Timeout includes the time to start streaming
app.get("/stream/logs", async (c) => {
// If this function takes longer than 60 seconds to start,
// the request will timeout
const logs = await prepareLogs();
// Streaming the response itself is included in the timeout
return c.stream(async (write) => {
for (const log of logs) {
await write(`${log}\n`);
}
});
});
Validation
The timeout value must be a positive number. Invalid configurations are caught at middleware creation time.
import { timeout } from "@mage/app/timeout";
// Valid
timeout({ ms: 1000 }); // 1 second
timeout({ ms: 30000 }); // 30 seconds
timeout(); // Default: 30 seconds
// Invalid - throws error
timeout({ ms: 0 }); // Throws: must be positive
timeout({ ms: -1000 }); // Throws: must be positive
Notes
- Place early in middleware chain to cover all downstream handlers
- Measures server processing time (not network latency)
- Use route-specific middleware for different timeout limits per endpoint
Related
- Middleware - How middleware works in Mage
- MageContext - Working with requests and responses
- Error Handling - Handling errors in your application