Deno, as a modern alternative to Node.js, offers numerous improvements and innovations, particularly in security, module systems, and developer experience. Although still in development, Deno is a compelling choice for developers seeking a simple, secure, and modern JavaScript/TypeScript development environment. As its community continues to grow, Deno’s potential and influence are expected to expand further.
Deno Fundamentals
- Built-in Security Model: Deno enforces strict permission controls, requiring explicit permissions for actions like file read/write or network access.
- TypeScript Support: Deno natively supports TypeScript, providing enhanced type checking and a better development experience.
- ES Modules: Deno uses URLs or import maps for module imports, differing from Node.js’s CommonJS module system.
Command-Line Tools
-
deno run
: Executes a single file. -
deno test
: Runs tests. -
deno fmt
: Formats code. -
deno lint
: Checks code style.
Configuration File
Deno’s configuration is primarily managed through the deno.json
file, which allows customization of runtime behavior. Environment variables mainly affect Deno’s global configuration, such as logging levels or cache directories.
Deno Configuration File (deno.json)
The configuration file typically includes the following sections:
- permissions: Defines runtime permissions.
- importMap: Configures module import mappings.
- compilerOptions: TypeScript compiler options.
- lintRules: Linting rules (if using deno-lint).
- watch: Monitors file changes and automatically re-runs.
- reload: Automatically reloads modules.
{
"permissions": {
"env": true,
"net": ["*"],
"read": ["./data"],
"write": ["./output"]
},
"importMap": {
"imports": {
"lodash": "https://cdn.skypack.dev/lodash@4.17.21",
"my-local-module": "./src/my-local-module.ts"
}
},
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["dom", "deno.ns"],
"strict": true
},
"lintRules": {
// ...
},
"watch": true,
"reload": {
"enabled": true,
"include": ["./src"]
}
}
Environment Variables
-
DENO_DIR
: Specifies the configuration, cache, and download directory, defaulting to~/.deno
. -
DENO_AUTH_TOKEN
: Authentication token for accessing private modules. -
DENO_LOGGING_LEVEL
: Controls logging level, e.g.,debug
,info
,warn
,error
. -
DENO_CACHE
: Custom cache directory. -
DENO.land_proxy
: Proxy settings for accessing deno.land. -
DENO_NO_COLOR
: Disables colored output if set.
# Linux/MacOS
export DENO_DIR=/path/to/custom/deno/dir
export DENO_LOGGING_LEVEL=debug
# Windows
set DENO_DIR=%USERPROFILE%\custom\deno\dir
set DENO_LOGGING_LEVEL=debug
Note that not all configuration options can be set via environment variables; most configurations are defined in the deno.json
file. Deno’s permissions are typically specified at runtime via command-line flags, not through configuration files or environment variables. For example: deno run --allow-read=./data your_script.ts
.
Creating an HTTP Server
Creating an HTTP server in Deno is straightforward using the built-in std/http
library.
// server.ts
import { serve } from "https://deno.land/std/http/server.ts";
const s = serve({ port: 8000 });
console.log("Server is running on http://localhost:8000");
for await (const req of s) {
req.respond({ status: 200, body: "Hello, World!\n" });
}
Code Breakdown:
-
Import the
serve
Function:import { serve } from "https://deno.land/std/http/server.ts";
imports theserve
function from Deno’s standard library to create an HTTP server. -
Start the Server:
const s = serve({ port: 8000 });
creates and starts a server listening on port 8000.s
is an iterable object representing each incoming request. -
Log Server Info:
console.log("Server is running on http://localhost:8000");
informs users that the server is running and provides the access URL. -
Handle Requests:
for await (const req of s)
is an async iterator that waits for and processes each incoming HTTP request.req
is aServerRequest
instance containing request details. -
Respond to Requests:
req.respond({ status: 200, body: "Hello, World!\n" });
sends a response to the client with a 200 status code and a body of "Hello, World!\n". -
Run the Server: In the terminal, run the script with
deno run --allow-net server.ts
. The--allow-net
flag is required to allow network access.
Fetching Remote Data
Fetching remote data in Deno typically uses the fetch
API:
// fetch_data.ts
import { assert } from "https://deno.land/std/testing/asserts.ts";
import { json as parseJson } from "https://deno.land/std/io/ioutil.ts";
async function fetchData(url: string) {
const response = await fetch(url);
assert(response.ok, `Failed to fetch data: ${response.statusText}`);
const data = await parseJson(await response.text());
console.log(data);
}
// Example URL, replace with your desired data source
const remoteDataURL = "https://jsonplaceholder.typicode.com/todos/1";
fetchData(remoteDataURL);
Code Breakdown:
Importing Modules:
-
assert
ensures the HTTP request succeeds. -
parseJson
converts received text to a JSON object.
Defining the fetchData
Function:
-
fetch(url)
asynchronously sends an HTTP GET request to the specified URL. -
response.ok
checks if the HTTP status code is in the 200-299 range, indicating success. -
response.text()
retrieves the response body as text. -
parseJson(text)
parses the text into a JSON object. -
console.log(data)
logs the parsed data.
Calling fetchData
:
-
fetchData(remoteDataURL)
calls the function with an example URL to fetch remote data.
Running the Script:
- Run with
deno run --allow-net fetch_data.ts
. The--allow-net
flag is required for network access.
File System Operations
// file_operations.ts
import { readTextFile, writeTextFile } from "https://deno.land/std/fs/mod.ts";
// Read a file
const content = await readTextFile("example.txt");
console.log(content);
// Write to a file
const newContent = "This is new content.";
await writeTextFile("example.txt", newContent);
Network Programming
// http_server.ts
import { serve } from "https://deno.land/std/http/server.ts";
const s = serve({ port: 8000 });
console.log("Server is running on http://localhost:8000");
for await (const req of s) {
req.respond({ status: 200, body: "Hello, World!\n" });
}
Asynchronous Programming
Deno uses async/await
syntax for asynchronous operations, making code cleaner and easier to understand.
// async_example.ts
import { delay } from "https://deno.land/std/async/mod.ts";
async function asyncTask() {
console.log("Task started...");
await delay(1000); // Delay for 1 second
console.log("Task completed.");
}
asyncTask();
Asynchronous File Operations
// async_file.ts
import { ensureDir, readTextFile, writeTextFile } from "https://deno.land/std/fs/mod.ts";
import { delay } from "https://deno.land/std/async/mod.ts";
async function asyncFileOps() {
try {
await ensureDir("output"); // Ensure directory exists
const content = await readTextFile("input.txt");
console.log("Read content:", content);
const newContent = "New content";
await writeTextFile("output/output.txt", newContent);
console.log("Wrote new content to output file.");
await delay(2000); // Delay for 2 seconds
console.log("Finished async operations.");
} catch (err) {
console.error("An error occurred:", err);
}
}
asyncFileOps();
Modules and Standard Library
Deno’s module system is based on ES Modules, allowing code import and export via URLs. Deno’s standard library provides useful modules for file system operations, networking, HTTP servers, JSON processing, encryption, and more.
Importing Standard Library Modules:
// import_std.ts
import { readTextFile } from "https://deno.land/std/fs/mod.ts";
import { serve } from "https://deno.land/std/http/server.ts";
// Read a file using readTextFile
const content = await readTextFile("example.txt");
console.log(content);
// Create an HTTP server
const s = serve({ port: 8000 });
console.log("Server is running on http://localhost:8000");
for await (const req of s) {
req.respond({ status: 200, body: "Hello, World!\n" });
}
Custom Modules:
// my_module.ts
export function add(a: number, b: number): number {
return a + b;
}
// Import in another file
// import_ts.ts
import { add } from "./my_module.ts";
console.log(add(2, 3)); // Outputs 5
JSON Processing in the Standard Library:
// json_example.ts
import { readTextFile } from "https://deno.land/std/fs/mod.ts";
import { json as parseJson } from "https://deno.land/std/json/mod.ts";
const jsonData = await readTextFile("data.json");
const data = parseJson(jsonData);
console.log(data);
Network Operations in the Standard Library:
// net_example.ts
import { connect } from "https://deno.land/std/net/tcp.ts";
const conn = await connect({ hostname: "localhost", port: 8000 });
conn.write(new TextEncoder().encode("GET / HTTP/1.1\r\nHost: localhost:8000\r\n\r\n"));
const response = new TextDecoder().decode(await Deno.readAll(conn));
console.log(response);
conn.close();
Using Third-Party Modules from deno.land/x:
// third_party_module.ts
import { log } from "https://x.nest.land/log@0.1.0/mod.ts";
log.info("This is an info message");
Deno’s standard library and third-party modules are typically imported via HTTPS URLs, providing version control and security. deno.land
is a module registry similar to npm but designed for Deno. x.nest.land
is another Deno module repository hosting community-maintained modules.
Using WebSocket
Deno supports WebSocket through its standard library or third-party libraries. The following example uses the ws
third-party library, popular for both Deno and Node.js.
Install the ws
library:
deno install -A -f --unstable --name deno_ws https://deno.land/x/ws@v1.1.0/mod.ts
Server-Side Code
Create a WebSocket server to listen for client connections and send messages to connected clients.
// server.ts
import { Server } from "deno_ws/mod.ts";
const server = new Server({ port: 8080 });
server.on("connection", (socket) => {
console.log("Client connected");
socket.on("message", (message) => {
console.log(`Received message => ${message}`);
socket.send(`You sent -> ${message}`);
});
socket.on("close", () => {
console.log("Client disconnected");
});
});
console.log("WebSocket server is running on ws://localhost:8080");
Client-Side Code
Create a WebSocket client to connect to the server and send/receive messages.
// client.ts
import { connect } from "deno_ws/mod.ts";
const socket = connect("ws://localhost:8080");
socket.on("open", () => {
console.log("Connected to WebSocket server");
socket.send("Hello, Server!");
});
socket.on("message", (message) => {
console.log(`Received from server: ${message}`);
});
socket.on("close", () => {
console.log("Connection closed");
});
Running the Example
Open two terminal windows.
In the first window, run the server:
deno run --allow-net server.ts
In the second window, run the client:
deno run --allow-net client.ts
Server-Side:
- Imports the
Server
class and creates an instance listening on port 8080. - Handles new client connections with the
connection
event, logging and setting up a message handler. - Responds to each received message with a confirmation message.
- Logs when a client disconnects via the
close
event.
Client-Side:
- Uses the
connect
function to connect to the server’s URL. - Sets an
open
event handler to send a message when the connection is established. - Sets a
message
event handler to receive and log server messages. - Sets a
close
event handler to log connection closure.
Error Handling and Debugging
Error Handling
Deno uses try/catch
for error handling, supporting asynchronous error handling.
// error_handling.ts
import { readFile } from "https://deno.land/std/fs/mod.ts";
try {
const data = await readFile("non_existent_file.txt");
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
console.error("File not found:", error);
} else {
throw error; // Re-throw unhandled errors
}
}
Debugging
Debugging in Deno can be done using console.log
, console.error
, and the debugger
statement with an IDE or browser developer tools.
// debug.ts
function debugFunction(value) {
debugger; // Pauses execution, allowing context inspection in a debugger
console.log("Debugging value:", value);
}
debugFunction("Debug me");
Performance Optimization
- Avoid Unnecessary Computations: Compute values only when needed, avoiding premature calculations.
- Use Asynchronous Operations: For I/O-intensive tasks, use async operations to avoid blocking the main thread.
- Cache Results: Cache results of repetitive computations.
- Leverage Type Checking: TypeScript’s type system helps prevent runtime errors and improves code quality.
- Restrict Permissions: Deno’s permission model allows precise control over code access, minimizing resource waste.
Here’s an optimization example using deno.cache
to cache imported modules:
// optimized_import.ts
import { cache } from "https://deno.land/x/deno.land_std@0.125.0/cache/mod.ts";
const cachedModule = await cache(
"https://deno.land/x/your_module@latest",
".cache",
);
// Import from cache
import * as mod from `${cachedModule}/mod.ts`;
👉 Click to join and systematically improve development capabilities: Advanced Development Learning
Top comments (0)