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
serveFunction:import { serve } from "https://deno.land/std/http/server.ts";imports theservefunction 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.sis 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.reqis aServerRequestinstance 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-netflag 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:
-
assertensures the HTTP request succeeds. -
parseJsonconverts received text to a JSON object.
Defining the fetchData Function:
-
fetch(url)asynchronously sends an HTTP GET request to the specified URL. -
response.okchecks 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-netflag 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
Serverclass and creates an instance listening on port 8080. - Handles new client connections with the
connectionevent, logging and setting up a message handler. - Responds to each received message with a confirmation message.
- Logs when a client disconnects via the
closeevent.
Client-Side:
- Uses the
connectfunction to connect to the server’s URL. - Sets an
openevent handler to send a message when the connection is established. - Sets a
messageevent handler to receive and log server messages. - Sets a
closeevent 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)