Skip to main content
โšก Calmops

Deno: The Modern JavaScript Runtime

Deno (pronounced “deno”) is a modern JavaScript runtime created by Ryan Dahl, the original creator of Node.js. Built in Rust with V8, Deno aims to fix design mistakes in Node.js while providing a secure, modern development experience.

What is Deno?

Deno is a JavaScript runtime that provides:

  • Native TypeScript support - No transpilation needed
  • Secure by default - Explicit permission system
  • ES Modules - URL-based module loading
  • Built-in tooling - Linter, formatter, test runner
  • URL-based imports - No node_modules
# Install Deno
curl -fsSL https://deno.land/install.sh | sh

# Windows (via PowerShell)
irm https://deno.land/install.ps1 | iex

# Run TypeScript directly
deno run app.ts

# Run JavaScript
deno run app.js

Key Features

1. Native TypeScript Support

// app.ts - Direct execution
interface User {
  id: number;
  name: string;
  email: string;
}

const users: User[] = [
  { id: 1, name: "Alice", email: "[email protected]" },
  { id: 2, name: "Bob", email: "[email protected]" }
];

users.forEach(user => {
  console.log(`${user.name}: ${user.email}`);
});

// Export for other modules
export { users };

2. Security by Default

Deno runs in a sandbox with no file system or network access by default:

// This will fail - no permissions
const file = await Deno.readTextFile("secret.txt");
// Error: Permission denied

// Request permissions at runtime
const permission = await Deno.permissions.query({ name: "read", path: "./config.json" });
# Grant permissions explicitly
deno run --allow-read app.ts
deno run --allow-net --allow-env app.ts

# Multiple permissions
deno run -A app.ts  # All permissions (not recommended)
deno run --allow-read=. --allow-net=localhost:3000 app.ts

3. URL-Based Imports

// Import from URLs
import { serve } from "https://deno.land/[email protected]/http/server.ts";
import lodash from "https://cdn.skypack.dev/lodash";

// Import from npm (new in Deno 1.25+)
import express from "npm:express@4";

4. Built-in Tools

# Format code
deno fmt

# Lint
deno lint

# Type check
deno check app.ts

# Run tests
deno test

# Bundle
deno bundle app.ts app.js

# Doc generation
deno doc app.ts

Deno vs Node.js

Feature Deno Node.js
Language Rust C++
Engine V8 V8
TypeScript Native Via transpiler
Package Manager URL imports/npm npm
ES Modules Default CommonJS default
Permissions Explicit grants Full access
Top-level await Yes Yes (ESM)
Worker Threads Web Workers worker_threads
Type Checking Built-in Via TypeScript

Getting Started

Hello World

// hello.ts
console.log("Hello from Deno!");

// Run it
deno run hello.ts

HTTP Server

// server.ts
const handler = (request: Request): Response => {
  return new Response("Hello from Deno!", {
    headers: { "Content-Type": "text/plain" }
  });

Deno.serve(handler);

// Alternative using std library
import { serve } from "https://deno.land/[email protected]/http/server.ts";

serve(handler);

File Operations

// Read file
const text = await Deno.readTextFile("example.txt");

// Write file
await Deno.writeTextFile("output.txt", "Hello World");

// Check if file exists
const exists = await Deno.stat("example.txt").then(() => true).catch(() => false);

// Read as JSON
const config = JSON.parse(await Deno.readTextFile("config.json"));

Working with JSON

import { serve } from "https://deno.land/[email protected]/http/server.ts";

const api = serve(async (req) => {
  const data = {
    message: "Hello API",
    timestamp: new Date().toISOString(),
    version: "1.0.0"
  };
  
  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" }
  });
});

serve(api);

Deno Modules

Using Standard Library

// Import from Deno's standard library
import { 
  readAll, 
  writeAll 
} from "https://deno.land/[email protected]/streams/conversion.ts";

import { 
  parse as parseArgs 
} from "https://deno.land/[email protected]/flags/mod.ts";

import { 
  glob 
} from "https://deno.land/[email protected]/fs/glob.ts";

Using npm Packages

// Direct npm imports (Deno 1.25+)
import express from "npm:express@4";
import cowsay from "npm:cowsay@1";

const app = express();

app.get("/", (req, res) => {
  res.send(cowsay.say({ text: "Hello from Deno!" }));
});

app.listen(3000);

Using Third-Party Modules

// Oak framework
import { Application, Router } from "https://deno.land/x/[email protected]/mod.ts";

const app = new Application();
const router = new Router();

router.get("/", (ctx) => {
  ctx.response.body = "Hello from Oak!";
});

app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 3000 });

Testing in Deno

Writing Tests

import { assertEquals, assertExists } from "https://deno.land/[email protected]/testing/asserts.ts";

Deno.test("simple test", () => {
  assertEquals(1 + 1, 2);
});

Deno.test("async test", async () => {
  const data = await fetch("https://api.example.com/data");
  assertEquals(data.ok, true);
});

Deno.test("test with setup", async (t) => {
  const server = await startTestServer();
  
  await t.step("test endpoint", async () => {
    const res = await fetch(server.url);
    assertEquals(res.status, 200);
  });
  
  await server.stop();
});

Running Tests

# Run all tests
deno test

# Run specific file
deno test my_test.ts

# Run with coverage
deno test --coverage

# Watch mode
deno test --watch

Deno Deploy

Deploying to Deno Deploy

// deploy.ts
export default {
  fetch(request: Request): Response {
    return new Response("Deployed on Deno Deploy!");
  }
};
# Deploy via CLI
deno deploy deploy.ts

# Or use GitHub integration
# Connect your GitHub repo to Deno Deploy

KV Database

// Deno KV - built-in key-value store
const kv = await Deno.openKv();

await kv.set(["users", "alice"], { name: "Alice", email: "[email protected]" });

const user = await kv.get(["users", "alice"]);
console.log(user.value);

// Query
const iter = kv.list({ prefix: ["users"] });
for await (const res of iter) {
  console.log(res.key, res.value);
}

Deno Fresh Framework

Creating a Fresh Project

# Create new Fresh project
deno run -A -r https://fresh.deno.dev my-project

cd my-project
deno task start

Fresh Route Structure

// routes/index.tsx
import { PageProps } from "$fresh/server.ts";

export default function Home({ url }: PageProps) {
  return (
    <html>
      <head>
        <title>Fresh App</title>
      </head>
      <body>
        <h1>Hello Fresh!</h1>
        <p>Current path: {url.pathname}</p>
      </body>
    </html>
  );
}

Interactive Islands

// islands/Counter.tsx
import { useState } from "preact/hooks";

export function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Environment Variables

Using dotenv

// Load .env file
import "https://deno.land/x/dotenv/load.ts";

console.log(Deno.env.get("DATABASE_URL"));
# .env file
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret123
# Or via CLI
deno run --env -A app.ts

Configuration

deno.json

{
  "compilerOptions": {
    "allowJs": true,
    "lib": ["deno.window"],
    "strict": true
  },
  "imports": {
    "$std/": "https://deno.land/[email protected]/",
    "$fresh/": "https://deno.land/x/[email protected]/"
  },
  "tasks": {
    "dev": "deno run -A --watch=static/,routes/ dev.ts",
    "build": "deno run -A dev.ts build",
    "start": "deno run -A main.ts"
  },
  "lint": {
    "include": ["routes/"],
    "rules": {
      "tags": ["fresh", "recommended"]
    }
  },
  "exclude": ["**/_fresh/*"]
}

Common Patterns

Error Handling

try {
  const data = await Deno.readTextFile("data.json");
} catch (error) {
  if (error instanceof Deno.errors.NotFound) {
    console.log("File not found");
  } else {
    console.error("Error:", error);
  }
}

Workers

// main.ts
const worker = new Worker(
  new URL("./worker.ts", import.meta.url).href,
  { type: "module" }
);

worker.onmessage = (e) => {
  console.log("Result:", e.data);
};

worker.postMessage("Hello worker");

// worker.ts
self.onmessage = (e) => {
  self.postMessage(`Processed: ${e.data}`);
};

HTTP Client

const response = await fetch("https://api.example.com/data", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Alice" }),
});

const data = await response.json();

Migration from Node.js

CommonJS to ESM

// Node.js CommonJS
const fs = require("fs");
const path = require("path");
const { foo } = require("./module");

// Deno ESM
import * as fs from "https://deno.land/[email protected]/node/fs.ts";
import { join } from "https://deno.land/[email protected]/node/path.ts";
import { foo } from "./module.ts";

Package.json to Imports

// Node.js package.json
{
  "dependencies": {
    "express": "^4.18.0",
    "lodash": "^4.17.0"
  }
}
// Deno - use imports in code
import express from "npm:express@4";
import _ from "npm:lodash@4";

// Or create deno.json imports
{
  "imports": {
    "express": "npm:express@4",
    "lodash": "npm:lodash@4"
  }
}

When to Use Deno

Good Use Cases

  • TypeScript projects - Native TS support
  • Security-sensitive apps - Permission system
  • Edge deployment - Deno Deploy, Cloudflare Workers
  • Modern tooling - Built-in linter, formatter
  • Fresh framework - Island architecture
  • Simple projects - No node_modules, fast startup

When to Stick with Node.js

  • Enterprise apps - Larger ecosystem
  • Legacy projects - CommonJS compatibility
  • Windows server - Better Node.js support
  • Native modules - Some packages don’t work in Deno
  • Team familiarity - Existing Node.js expertise

External Resources

Comments