@nextrush/adapter-denoDeno
Deno HTTP adapter for NextRush — connects your application to Deno.serve().
Connect your NextRush application to Deno's native Deno.serve() API. This adapter bridges NextRush's middleware system to Deno's HTTP server.
Source & internals
README · ARCHITECTURE · see also Adapter Contract
| Package | @nextrush/adapter-deno |
| Status | Stable |
| Support tier | Public — stable, semver-guarded |
Included in nextrush? | No — standalone install |
| Runtime | Deno >=2.0 only |
Installation
Import directly in Deno (no npm required):
import { createApp } from 'npm:@nextrush/core';
import { serve } from 'npm:@nextrush/adapter-deno';Or with import maps in deno.json:
{
"imports": {
"@nextrush/core": "npm:@nextrush/core",
"@nextrush/adapter-deno": "npm:@nextrush/adapter-deno"
}
}Deno Permissions Required
Deno requires explicit network permission to run an HTTP server. Use --allow-net at minimum. See
Deno Permissions for details.
Quick Start
import { createApp } from '@nextrush/core';
import { listen } from '@nextrush/adapter-deno';
const app = createApp();
app.use(async (ctx) => {
ctx.json({ runtime: 'deno', message: 'Hello World!' });
});
listen(app, 8080);
// Output: 🚀 NextRush listening on http://localhost:8080 (Deno)Run with Deno:
deno run --allow-net src/main.tsAPI Reference
serve(app, options?)
Start the HTTP server with full configuration control.
import { createApp } from '@nextrush/core';
import { serve } from '@nextrush/adapter-deno';
const app = createApp();
const server = serve(app, {
port: 8080,
host: '0.0.0.0',
onListen: ({ port, hostname }) => {
console.log(`Server running at http://${hostname}:${port}`);
},
onError: (error) => {
console.error('Server error:', error);
},
});ServeOptions
ServeOptions
| Property | Type | Description |
|---|---|---|
port | number= 8080 | Port to listen on |
host | string= '0.0.0.0' | Host to bind to |
onListen? | (info: { port: number; host: string; hostname: string }) => void | Callback when server starts listening |
onError? | (error: Error) => void | Custom error handler for uncaught server errors |
cert? | string | TLS certificate in PEM format. Deprecated — use tls.cert instead; removed in a future minor version. |
key? | string | TLS private key in PEM format. Deprecated — use tls.key instead; removed in a future minor version. |
tls? | { cert: string; key: string; ca?: string } | TLS configuration (canonical shape — matches @nextrush/adapter-bun). Deno.serve() negotiates HTTP/1.1 vs HTTP/2 via ALPN automatically once this is present. Prefer this over the deprecated flat cert/key fields. |
shutdownTimeout | number= 30000 | Grace period in ms for draining in-flight requests during shutdown |
gracefulShutdown? | boolean | GracefulShutdownOptions | Opt-in: wire SIGTERM/SIGINT to the same close() drain logic. `true` installs handlers for the default signal set; `{ signals, timeout }` overrides them. Omitted by default — no signal handler is installed and process behavior is unchanged. |
ServerInstance
The returned server instance provides control methods:
interface ServerInstance {
server: DenoServer; // Underlying Deno server
port: number; // Actual port
hostname: string; // Actual hostname
close(): Promise<void>; // Graceful shutdown
address(): { port; hostname };
finished: Promise<void>; // Resolves when server stops
}listen(app, port?)
Quick-start shorthand with default logging.
import { createApp } from '@nextrush/core';
import { listen } from '@nextrush/adapter-deno';
const app = createApp();
const server = listen(app, 8080);
// Output: 🚀 NextRush listening on http://localhost:8080 (Deno)createHandler(app)
Create a raw handler for advanced use cases.
import { createApp } from '@nextrush/core';
import { createHandler } from '@nextrush/adapter-deno';
const app = createApp();
const handler = createHandler(app);
// Use directly with Deno.serve
Deno.serve({ handler, port: 8080 });TLS/HTTPS & HTTP/2
Enable HTTPS with TLS certificates. Deno.serve() negotiates HTTP/1.1 vs HTTP/2 via ALPN
automatically once TLS is configured — there is no separate protocol option.
import { serve } from '@nextrush/adapter-deno';
const server = serve(app, {
port: 443,
tls: {
cert: await Deno.readTextFile('./cert.pem'),
key: await Deno.readTextFile('./key.pem'),
},
});Migrating from the flat cert/key fields
The top-level cert/key options are deprecated in favor of tls (matching
@nextrush/adapter-bun's shape) and will be removed in a future minor version:
// Before (deprecated)
serve(app, { cert, key });
// After
serve(app, { tls: { cert, key } });Deno Permissions
NextRush requires network permission to run:
# Minimum required
deno run --allow-net app.ts
# With file access (for static files, etc.)
deno run --allow-net --allow-read app.ts
# Development (more permissive)
deno run --allow-all app.tsServer Lifecycle
Waiting for Server Finish
Deno adapter exposes a finished promise:
const server = serve(app, { port: 8080 });
// Wait for server to complete (useful in scripts)
await server.finished;Graceful Shutdown
Handle server shutdown properly:
const server = serve(app, { port: 8080 });
Deno.addSignalListener('SIGTERM', async () => {
console.log('Shutting down...');
await server.close(); // Closes app + server
Deno.exit(0);
});Or wire it declaratively with gracefulShutdown instead of listening for signals yourself:
const server = await serve(app, {
port: 8080,
gracefulShutdown: true, // installs SIGTERM + SIGINT, using shutdownTimeout as the drain window
});
// Or override the signal set / drain timeout explicitly:
const server2 = await serve(app, {
port: 8080,
gracefulShutdown: { signals: ['SIGTERM'], timeout: 5_000 },
});Omitting gracefulShutdown installs no signal handler — process behavior is unchanged. The
handler is removed once close() completes, so repeated serve()/close() cycles never
accumulate duplicate listeners.
Client IP Access
The adapter extracts client IP from Deno's connection info:
app.use(async (ctx) => {
// Available via context
const ip = ctx.ip; // From remoteAddr
ctx.json({ clientIp: ip });
});Request Paths and Dot Segments
Dot segments are rejected here — this platform preserves the target
Deno.serve() hands the request target to the application unparsed, so a target containing a dot
segment — /files/../admin, or the encoded /files/%2e%2e/admin — is rejected with 400 before
any middleware runs, and ctx.path holds the bytes the client sent.
That guarantee ends at this process's edge: a proxy or platform in front that resolves the
segment first makes it unreachable. Authorise path rules against ctx.path, and reject unwanted
raw targets in front of Deno. See
Request Paths and Dot Segments.
Deno Deploy
For Deno Deploy, use createHandler:
import { createApp } from '@nextrush/core';
import { createHandler } from '@nextrush/adapter-deno';
const app = createApp();
app.use(async (ctx) => {
ctx.json({
message: 'Hello from Deno Deploy!',
region: Deno.env.get('DENO_REGION'),
});
});
Deno.serve(createHandler(app));TypeScript
Full TypeScript support with Deno's native TS:
import type { Application } from '@nextrush/core';
import type { ServeOptions, ServerInstance } from '@nextrush/adapter-deno';
import { serve, listen, createHandler } from '@nextrush/adapter-deno';
const options: ServeOptions = {
port: 8080,
hostname: '0.0.0.0',
};
const server: ServerInstance = serve(app, options);Related
- Platforms Overview — Understanding runtime adapters
- @nextrush/adapter-node — Node.js adapter
- @nextrush/adapter-bun — Bun adapter
- @nextrush/adapter-edge — Edge runtime adapter