Getting StartedChoose Your Runtime

Deno

Install NextRush on Deno, run a hello-world server on Deno.serve(), and see what Deno's permission model means for your NextRush app.

Runtime · Secure by default

Secure by default, permission model. Deno refuses to do anything without explicit permission — no filesystem, network, or environment access until you grant it with a flag. Same createApp/createRouter API as Node and Bun, running on Deno.serve(). The tradeoff: every deno run needs the right --allow-* flags, or the process denies the request before your route ever runs.

Secure by defaultPermission model~10 minBeginnerStable adapter

Where Deno fits

RuntimeBest forTradeoff
NodeProduction APIs, long-running servers, full ecosystemHeavier footprint than Bun or edge
BunLocal dev speed, native TypeScript, fast cold startYounger ecosystem, some node:* gaps
Deno · this pageSecure by default, permission model, TS nativeSmaller package ecosystem
EdgeGlobal low latency, Cloudflare/VercelNo long-running server, node:* limited
ServerlessPay-per-request, auto-scalingCold starts, no persistent state

What changes from Node

SameDifferent
createApp()listen() import — comes from @nextrush/adapter-deno, not nextrush
createRouter()Runtime underneath — Deno.serve(), not node:http
Route registrationPermission flags — --allow-net required, --allow-read/--allow-env as needed
Context APInpm: specifier — Deno resolves npm packages directly
MiddlewareNo tsx needed — Deno runs .ts directly

"Partial" means the adapter is Stable but not yet verified against the real platform in CI (see the compatibility matrix) — not that it's missing features.

Targeting a different runtime? Node · Bun · Edge · Serverless each implement the same Context and Application contract. Not sure which one fits? See the runtime decision guide.

Before you begin

  • ✓ Deno installed (deno.com)
  • ✓ A terminal
  • ✓ ~10 minutes

Confirm your Deno version

  • Why this matters: @nextrush/adapter-deno targets Deno's stable Deno.serve() surface.
deno --version
# needs to read deno 2.x.x or higher

If Deno isn't installed yet, follow the install instructions before continuing.

Install NextRush and the Deno adapter

  • Why this matters: like Bun, the nextrush meta-package does not bundle a runtime adapter — you add @nextrush/adapter-deno alongside it.

Do: Deno resolves npm packages directly with the npm: specifier — no separate lockfile step needed for a quick script:

server.ts
import { createApp, createRouter } from 'npm:nextrush';
import { listen } from 'npm:@nextrush/adapter-deno';

Or pin the same imports in deno.json so every file in the project resolves them the same way:

deno.json
{
  "imports": {
    "nextrush": "npm:nextrush",
    "@nextrush/adapter-deno": "npm:@nextrush/adapter-deno"
  }
}

Behind the scenes:

  • createApp and createRouter still come from nextrush — runtime-independent
  • listen, serve, and createHandler come from @nextrush/adapter-deno — the meta-package's listen is wired to @nextrush/adapter-node specifically
  • Once imports are in deno.json, every file can write plain import { createApp } from 'nextrush' — the import map resolves the npm: specifier

Run a hello-world server

Create server.ts:

server.ts
import { createApp, createRouter } from 'nextrush';
import { listen } from '@nextrush/adapter-deno';

const app = createApp();
const router = createRouter();

router.get('/', (ctx) => {
  ctx.json({ runtime: 'deno', status: 'ok' });
});

app.route('/', router);
await listen(app, 8080);

Run it — Deno requires an explicit network permission:

deno run --allow-net server.ts

Expected result — curl http://localhost:8080/ returns {"runtime": "deno", "status": "ok"}

Behind the scenes:

  • listen(app, 8080) from @nextrush/adapter-deno calls Deno.serve() directly
  • Each request goes to NextRush's router through the same Context API Node and Bun use
  • ctx.runtime reads 'deno' because the adapter, not your handler, decides that value

--allow-net is not optional. Deno denies network access by default; omitting the flag fails the process before your route ever runs. Add --allow-read if you serve files from disk, or --allow-env if your app reads environment variables — grant only what your handlers actually use.

Why Deno's permissions matter

Your server is now running. Before moving on, it's worth understanding what Deno's permission model changes — and what stays the same.

Same: the Context API, createApp, createRouter, route registration, and middleware all behave identically to Node and Bun. Nothing in your route handler is Deno-specific.

Different: the --allow-* flags are a property of the Deno process, not something @nextrush/adapter-deno adds or works around. The adapter has no permission-checking code — it calls Deno.serve() the same way your own code would, and Deno's runtime enforces the sandbox before the adapter ever runs. A missing permission fails at the OS/runtime boundary, not inside NextRush.

One adapter-level behavior: @nextrush/adapter-deno adds its own request timeout (Promise.race-based, since Deno.serve() has no built-in per-request timeout) — defaults to 30 seconds, matching Node and Bun.

Deno runtime working — you're ready to verify and move on.

Deno baseline complete

Confirm the server answers:

curl -i http://localhost:8080/
# → HTTP/1.1 200 OK
# → {"runtime":"deno","status":"ok"}

Your project now looks like:

my-api/
  deno.json
  server.ts

Baseline complete — nothing ran that wasn't explicitly permitted, and the request still made it through.

What you learned

  • nextrush gives you the runtime-independent half — createApp, createRouter, and route registration are identical on Deno, Bun, and Node
  • listen/serve/createHandler come from @nextrush/adapter-deno, not the meta-package
  • ✓ Deno's permission flags are a runtime property, not a NextRush setting — --allow-net (and --allow-read/--allow-env as needed) gate the process before any NextRush code runs

The same app, every runtime

The code you just wrote runs on every runtime NextRush supports. Only the adapter import changes:

  1. createApp()
  2. Router
  3. Handler
  4. Deno adapter
  5. Running
// Node:      import { listen } from '@nextrush/adapter-node';
// Bun:       import { listen } from '@nextrush/adapter-bun';
// Deno:      import { listen } from '@nextrush/adapter-deno';    // ← you are here
// Edge:      import { serve } from '@nextrush/adapter-edge';
// Serverless import { handler } from '@nextrush/adapter-serverless';

Next steps

🚀 Build a Task API — recommended · ~20 min

Go past hello-world on the runtime you set up: routing, a JSON body, and a real 404. Start the tutorial →

Continue learning

Was this helpful?

On this page