Categories
Deno

Deno — OS Signals, File System Events, and Module Metadata

Deno is a new server-side runtime environment for running JavaScript and TypeScript apps.

In this article, we’ll take a look at how to get started with developing apps for Deno.

Handle OS Signals

We can handle signals with the Deno.signal method.

For example, we can write:

index.ts

console.log("Press Ctrl-C");
for await (const _ of Deno.signal(Deno.Signal.SIGINT)) {
  console.log("interrupted!");
  Deno.exit();
}

If we press Ctrl+C, we’ll trigger the sigint signal to interrupt the program.

We watch for the signal with the for-await-of loop.

Deno.Signal.SIGINT is the object for the sigint signal.

We call Deno.exit to exit the program.

We run the program by running:

deno run --unstable index.ts

Also, we can write it as a promise.

For instance, we can write:

index.ts

console.log("Press Ctrl-C to end the program");
await Deno.signal(Deno.Signal.SIGINT);
console.log("interrupted!");
Deno.exit();

to watch for the sigint signal.

Stop Watching Signals

We can stop watching signals by calling the sig.dispose method.

For example, we can write:

index.ts

const sig = Deno.signal(Deno.Signal.SIGINT);
setTimeout(() => {
  sig.dispose();
  console.log("No longer watching SIGINT signal");
}, 5000);

console.log("Watching SIGINT signals");
for await (const _ of sig) {
  console.log("interrupted");
}

In the setTimeout callback. we call the sig.dispose method to stop watching the sigint signal.

The for-await-of loop exits after 5 seconds when sig.dispose is called.

File System Events

We can watch for file system events with the Deno.watchFs method.

For example, we can write:

index.ts

const watcher = Deno.watchFs(".");
for await (const event of watcher) {
  console.log(event);
}

We watch the folder the script is in with Deno.watchFs .

We get the event from the event object.

Then we something like:

index.ts

{ kind: "create", paths: [ "/home/runner/IntelligentWorthwhileMice/./lock.json" ] }
{ kind: "modify", paths: [ "/home/runner/IntelligentWorthwhileMice/./lock.json" ] }
{ kind: "access", paths: [ "/home/runner/IntelligentWorthwhileMice/./lock.json" ] }
{
  kind: "create",
  paths: [ "/home/runner/IntelligentWorthwhileMice/./.3s18gah600nwj.foo.txt~" ]
}
{
  kind: "access",
  paths: [ "/home/runner/IntelligentWorthwhileMice/./.3s18gah600nwj.foo.txt~" ]
}
{
  kind: "modify",
  paths: [ "/home/runner/IntelligentWorthwhileMice/./.3s18gah600nwj.foo.txt~" ]
}
{ kind: "modify", paths: [ "/home/runner/IntelligentWorthwhileMice/./foo.txt" ] }
{
  kind: "modify",
  paths: [
    "/home/runner/IntelligentWorthwhileMice/./.3s18gah600nwj.foo.txt~",
    "/home/runner/IntelligentWorthwhileMice/./foo.txt"
  ]
}

displayed.

Then we can run it with:

deno run --allow-read index.ts

to watch for any file system events in the folder.

Module Metadata

We can get module meta with the import.meta property.

For example, we can write:

console.log(import.meta.url);
console.log(Deno.mainModule);
console.log(import.meta.main);

Then we see something like:

file:///home/runner/IntelligentWorthwhileMice/index.ts
file:///home/runner/IntelligentWorthwhileMice/index.ts
true

from the console output when we run:

deno run --allow-read --unstable index.ts

import.meta.url and Deno.mainModule both get the path of the module.

And import.meta.main returns true if it’s the entry point module.

Conclusion

We can handle OS signals, get module metadata, and watch file system events with Deno.

Categories
Deno

Deno — HTTP, Server, File Server, and Subprocesses

Deno is a new server-side runtime environment for running JavaScript and TypeScript apps.

In this article, we’ll take a look at how to get started with developing apps for Deno.

Simple HTTP Web Server

We can create a simple HTTP server with the server module provided by Deno.

For example, we can write:

import { serve } from "https://deno.land/std@0.75.0/http/server.ts";

const server = serve({ hostname: "0.0.0.0", port: 8080 });
console.log(`HTTP server running on port 8080`);

for await (const request of server) {
  const bodyContent = `Your user-agent is: ${request.headers.get("user-agent") || "Unknown"}`;
  request.respond({ status: 200, body: bodyContent });
}

We import the serve function to create our HTTP server.

The object has the hostname and port to listen to requests from.

Then we create the response body in the for-await-of loop.

We get the header by calling request.headers.get .

Then we call request.response with the HTTP status code and body in the object that we use as the argument of respond .

Then we can run the script with:

deno run --allow-net index.ts

Then when we go to http://localhost:8080, we should see something like:

Your user-agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36

File Server

We can also create a file server easily with Deno.

To do this, we just run:

deno run --allow-net --allow-read https://deno.land/std@0.75.0/http/file_server.ts

Then when we go to http://localhost:4507/, we see the files in the current folder listed.

TCP Echo Server

We can create a TCP echo server easily with the Deno.listen and Deno.copy methods.

For example, we can write:

const listener = Deno.listen({ port: 8080 });
console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
  Deno.copy(conn, conn);
}

to listen to port 8080.

Then we loop through the listener array to watch for requests.

Then we call Deno.copy to redirect the connection data inbound.

Creating a Subprocess

We can create a subprocess by using the Deno.run method.

For example, we can run:

const p = Deno.run({
  cmd: ["echo", "hello"],
});
await p.status();

to run echo hello .

Then we can wait for its completion with the p.status method.

We need the --allow-run flag to let us run subprocesses.

We can get results from subprocesses and run with them by using various methods.

For instance, we can write:

const fileNames = Deno.args;

const p = Deno.run({
  cmd: [
    "deno",
    "run",
    "--allow-read",
    "https://deno.land/std@0.75.0/examples/cat.ts",
    ...fileNames,
  ],
  stdout: "piped",
  stderr: "piped",
});

const { code } = await p.status();

if (code === 0) {
  const rawOutput = await p.output();
  await Deno.stdout.write(rawOutput);
} else {
  const rawError = await p.stderrOutput();
  const errorString = new TextDecoder().decode(rawError);
  console.log(errorString);
}

Deno.exit(code);

We call the status method to get the status code.

Then we write the output if the code is 0.

Otherwise, we output the error to the stderr .

And we log the string.

And finally, we call Deno.exit with the exit code of our command.

Conclusion

We can create a simple HTTP server, file server, and run subprocesses easily with Deno.

Categories
Deno

Deno — HTTP Requests and File Manipulation

Deno is a new server-side runtime environment for running JavaScript and TypeScript apps.

In this article, we’ll take a look at how to get started with developing apps for Deno.

Managing Dependencies

We can manage dependencies in our Deno apps.

We can reexport modules with the export statement:

math.ts

export {
  add,
  multiply,
} from "https://x.nest.land/ramda@0.27.0/source/index.js";

index.ts

import { add, multiply } from "./math.ts";

console.log(add(1, 2))
console.log(multiply(1, 2))

We reexport the items in math.ts and import it in index.ts .

Fetch Data

We can fetch data with the Fetch API with Deno.

The Fetch API is implemented with Deno.

For example, we can write:

const res = await fetch("https://yesno.wtf/api");
const json = await res.json()
console.log(json);

Then when we run:

deno run --allow-net index.ts

We get something like:

{
  answer: "yes",
  forced: false,
  image: "https://yesno.wtf/assets/yes/7-653c8ee5d3a6bbafd759142c9c18d76c.gif"
}

logged.

The --allow-net flag enables network communication in our script.

To get HTML, we can write:

const res = await fetch("https://deno.land/");
const html = await res.text()
console.log(html);

Then we download the HTML code from the deno.land URL.

To catch errors, we write:

try {
  await fetch("https://does.not.exist/");
} catch (error) {
  console.log(error.message)
}

Then we log the message from the error.message property.

Read and Write Files

Deno comes with an API for reading and writing files.

They come with both sync and async versions.

The --allow-read and --allow-write permissions are required to gain access to the file system.

For example, we can write:

index.ts

const text = await Deno.readTextFile("./people.json");
console.log(text)

people.json

[
  {
    "id": 1,
    "name": "John",
    "age": 23
  },
  {
    "id": 2,
    "name": "Sandra",
    "age": 51
  },
  {
    "id": 5,
    "name": "Devika",
    "age": 11
  }
]

Then when we run:

deno run --allow-read index.ts

We read the JSON file and log the data.

Deno.readTextFile returns a promise so we can use await .

Writing a Text File

To write a text file, we can use the writeTextFile method.

For example, we can write:

await Deno.writeTextFile("./hello.txt", "Hello World");
console.log('success')

to create hello.txt and use the string in the 2nd argument as the content.

Then when we run:

deno run --allow-write index.ts

We can also use the writeTextFileSync method to write text file synchronously.

For example, we can write:

try {
  Deno.writeTextFileSync("./hello.txt", "Hello World");
  console.log("Written to hello.txt");
} catch (e) {
  console.log(e.message);
}

Read Multiple Files

We can read multiple files by writing:

for (const filename of Deno.args) {
  const file = await Deno.open(filename);
  await Deno.copy(file, Deno.stdout);
  file.close();
}

Then when we run:”

deno run --allow-read index.ts foo.txt bar.txt

We should see the contents of foo.txt and bar.txt printed.

We call Deno.open to open each file.

Deno.args has the command line arguments, which is ['foo.txt', 'bar.txt'] .

And then we call Deno.copy to copy the content to stdout .

And then we close the filehandle with file.close .

Conclusion

We can make HTTP requests with the fetch API and read and write files with Deno.

Categories
Deno

Deno — Workers, Libraries, and Modules

Deno is a new server-side runtime environment for running JavaScript and TypeScript apps.

In this article, we’ll take a look at how to get started with developing apps for Deno.

Workers

Deno supports the web worker API.

For example, we can write:

index.ts

const worker = new Worker(new URL("worker.js", import.meta.url).href, {
  type: "module",
  deno: true,
});
worker.postMessage({ filename: "./log.txt" });

worker.js

self.onmessage = async (e) => {
  const { filename } = e.data;
  const text = await Deno.readTextFile(filename);
  console.log(text);
  self.close();
};

log.txt

hello

Then when we run:

deno run  --unstable --allow-read index.ts

We create the worker with the Worker constructor.

We pass in the URL to the worker.

And the object specifies the type as 'module' and deno set to true so that we can run the worker.

worker.js has the worker.

self is the worker instance. The onmessage method accepts message passed in by calling postMessage .

And then we call Deno.readTextFile method to read a file in the worker.

We then should see hello logged in the console.

Reloading Modules

Deno caches modules by default without fetching or recompiling it.

If we need to reload them, we run:

deno cache --reload index.ts

to reload everything used by index.ts .

We can also reload a specific module used by index.ts by running:

deno cache --reload=https://deno.land/std@0.75.0 index.ts

If we want to reload multiple modules used by index.ts , we run:

deno cache --reload=https://deno.land/std@0.75.0/fs/copy.ts,https://deno.land/std@0.75.0/fmt/colors.ts index.ts

Lock Files

Deno stores and check the subresource integrity for modules using a small JSON file.

The -lock=lock.json flag enables and specifies lock file checking.

To update the lock file, we run:

--lock=lock.json --lock-write

We can use the  — -lock=lock.json option to check the data when we run our app.

Standard Library

We should run pinned versions of imports to avoid unintended changes.

For example, instead of writing:

import { copy } from "https://deno.land/std/fs/copy.ts";

We should include the version number with the import by writing:

import { copy } from "https://deno.land/std@0.75.0/fs/copy.ts";

We may use libraries that have unstable Deno APIs.

The copy module we have above is unstable, so to use it, we run:

deno run --allow-read --allow-write --unstable main.ts

to enable unstable APIs.

Import and Export Modules

We can use standard ES modules to with Deno apps.

For example, we can write:

math.ts

export const add = (a: number, b: number) => a + b;
export const multiply = (a: number, b: number) => a * b;

index.ts

import { add, multiply } from "./math.ts";

console.log(add(1, 2))
console.log(multiply(1, 2))

We have math.ts that exports the add and multiply functions.

Then we use them in index.ts .

Remote Import

We can also import modules directly from a remote URL.

For example, we can write:

import {
  add,
  multiply,
} from "https://x.nest.land/ramda@0.27.0/source/index.js";

console.log(add(1, 2))
console.log(multiply(1, 2))

We import the add and multiple functions from https://x.nest.land/ramda@0.27.0/source/index.js .

Then we can use them the same way.

Conclusion

We can use web workers directly with Deno.

Also, we can import modules from various sources.

Categories
Deno

Deno — Command-Line and Permissions

Deno is a new server-side runtime environment for running JavaScript and TypeScript apps.

In this article, we’ll take a look at how to get started with developing apps for Deno.

Command-Line Interface

We can get help by running:

deno help

or:

deno -h

for short.

We can also write:

deno --help

to do the same thing.

Script Arguments

We can get command-line arguments in our script with the Deno.args property.

For example, we can write:

index.ts

console.log(Deno.args);

Then when we run:

deno run index.ts a b -c --quiet

We get:

[ "a", "b", "-c", "--quiet" ]

from the console output.

Permissions

Permission is required for running code that requires access to various resources.

They have to enabled specifically by default.

The following permissions available:

  • -A, — allow-all — Allow all permissions. This disables all security.
  •  — allow-env — Allow environment access for things like getting and setting of environment variables.
  •  — allow-hrtime — Allow high-resolution time measurement. High-resolution time can be used in timing attacks and fingerprinting.
  •  — allow-net=<url> — Allow network access. We can specify an optional, comma-separated list of domains to provide an allow-list of allowed domains.
  •  — allow-plugin — Allow loading plugins. Please note that — allow-plugin is an unstable feature.
  •  — allow-read=<allow-read> — Allow file system read access. We can specify an optional, comma-separated list of directories or files to provide an allow-list of allowed file system access.
  •  — allow-run — Allow running subprocesses. Be aware that subprocesses are not run in a sandbox and therefore do not have the same security restrictions as the Deno process. Therefore, use with caution.
  •  — allow-write=<allow-write> — Allow file system write access. You can specify an optional, comma-separated list of directories or files to provide an allow-list of allowed file system access.

For example, if we have a program that makes HTTP requests like:

const [url] = Deno.args;
const res = await fetch(url);
const body = new Uint8Array(await res.arrayBuffer());
await Deno.stdout.write(body);

We run it with:

deno run --allow-net index.ts https://yesno.wtf/api

to let the program access the Internet.

Permission APIs

We can query form permissions by using the Deno.permissions.query method with an object that has the permissions we want to query.

For example, we can write:

index.ts

const desc1 = { name: "read", path: "/foo" } as const;
console.log(await Deno.permissions.query(desc1));

const desc2 = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.query(desc2));

const desc3 = { name: "read", path: "/bar" } as const;
console.log(await Deno.permissions.query(desc3));

Then when we run:

deno run --unstable index.ts

We see:

PermissionStatus { state: "prompt" }
PermissionStatus { state: "prompt" }
PermissionStatus { state: "prompt" }

logged in the console.

We need the --unstable option to make the Deno.permissions.query method available.

We can revoke permissions by using the Deno.permissions.revoke method:

const desc = { name: "read", path: "/foo/bar" } as const;
console.log(await Deno.permissions.revoke(desc));

const strongDesc = { name: "read", path: "/foo" } as const;
await Deno.permissions.revoke(strongDesc);

Then we get:

PermissionStatus { state: "prompt" }

displayed.

Conclusion

We can get and set permissions with Deno.