DEV Community

Cover image for Deno and modules
Sebastien Filion
Sebastien Filion

Posted on • Edited on

Deno and modules

In this second article of the Deno series, I want to talk about modules.

This article is a transcript of a Youtube video I made.

If you used Node before -- or any server-side language for that matter -- you will know the gist about modules.
Splitting your code into modules can help you keep your project organized or enable library makers to
share their work.

Browser-side, modules have been available for about four years now.
Before, developers would add their namespace to the global object hoping there wouldn't be any collision.
If you are more of a front-end-type-person, you probably started using modules through babel or webpack.

Deno implements the ES modules specifications, which means we're importing stuff, not requiring stuff.
Working with ES modules is great because it means that Deno code can be ported to modern browser -- it isn't the case with Node code.

Alright -- let's fire up our editor and write some modules.

In this demo I will cover how to export and import modules and, how to ensure the integrity of your dependencies.
During this session, I will assume that you have a basic understanding of JavaScript and it's ecosystem. If you have any
question, don't hesitate to write them in the comment section bellow.


Take this code where we export a string and function.

// users.js
export const targetURL = "https://randomuser.me/api/";

export function getUser () {

  return fetch(`${targetURL}?inc=name,email,login,id`)
    .then(response => response.json())
    .then(({ results: [ user ] }) => user);
}
Enter fullscreen mode Exit fullscreen mode

We can import it like this using the relative path.

// scratch.js

import * as users from "./users.js";

users.getUser()
  .then(user => {
    console.log(`Welcome to ${user.name.title}. ${user.name.last}`);
  });
Enter fullscreen mode Exit fullscreen mode

You can use the Deno run subcommand with the appropriate flag.

deno run --allow-net=randomuser.me ./sratch.js
Enter fullscreen mode Exit fullscreen mode

You can also use the remote URL to the file when using a third-party dependency.

// scratch.js

import * as users from "https://raw.githubusercontent.com/sebastienfilion/i-y/main/deno/02/users.js";

Enter fullscreen mode Exit fullscreen mode

If the resource is on a private server, Deno has an environment variable DENO_AUTH_TOKENS that can be used as bearer token when making the request to the server.

// scratch.js

import * as users from "https://raw.githubusercontent.com/sebastienfilion/i-y-private/main/02/users.js";

Enter fullscreen mode Exit fullscreen mode
DENO_AUTH_TOKENS=████████████████████████████████████████@raw.githubusercontent.com deno run mod.js
Enter fullscreen mode Exit fullscreen mode

In the Deno community, there are some conventions when naming files. The first one is to have a mod.js file to export all public files -- it's a good practice when developing libraries.

// mod.js

export * from "./users.js";
Enter fullscreen mode Exit fullscreen mode

The second one is to have a deps.js file to import and expose third-party dependencies.

// deps.js

export * from "https://raw.githubusercontent.com/sebastienfilion/i-y/main/deno/02/mod.js";
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can create a JSON file to use as import-map for the dependencies. It's useful when you want to import dependencies by name instead of by URL.

{
  "imports": {
    "i-y": "https://raw.githubusercontent.com/sebastienfilion/i-y/main/02/mod.js"
  }
}
Enter fullscreen mode Exit fullscreen mode
// scratch.js

import * as users from "i-y";

Enter fullscreen mode Exit fullscreen mode
deno run --allow-net=randomuser.me --import-map="map.json" ./scratch.js
Enter fullscreen mode Exit fullscreen mode

Warning: import-map can't be composed; so if you are a library maker, try to avoid them.

When there's a new version of your dependencies available, you may need to refresh your cache.

// users.js
export const targetURL = "https://randomuser.me/api/";

export function getUser () {

  return fetch(`${targetURL}?inc=name,email,login,id`)
    .then(response => response.json())
    .then(({ results: [ { name: { title: "title, last: surname } } ] }) => ({ title, surname }));"
}
Enter fullscreen mode Exit fullscreen mode
// scratch.js

import * as users from "https://raw.githubusercontent.com/sebastienfilion/i-y/main/02/users.js";

users.getUser()
  .then(user => {
    console.log(`Welcome to ${user.title}. ${user.surname}`);
  });
Enter fullscreen mode Exit fullscreen mode

You can force the runtime to reload all dependencies with the --reload flag.

deno run --allow-net=randomuser.me --reload ./sratch.js
Enter fullscreen mode Exit fullscreen mode

Or you can be more granular and specify the dependencies you want to reload.

deno run --allow-net=randomuser.me --reload=https://raw.githubusercontent.com/sebastienfilion/i-y ./sratch.js
Enter fullscreen mode Exit fullscreen mode

Also, you can use the cache subcommand.

deno cache --reload
deno run --allow-net=randomuser.me ./sratch.js
Enter fullscreen mode Exit fullscreen mode

Finally, you can ensure integrity of your dependencies by writing a lock file.

deno cache --lock=lock.json --lock-write ./scratch.js
Enter fullscreen mode Exit fullscreen mode

If something happens to the dependencies, you'd get an error warning you of the changes...

deno run --lock=lock.json --allow-net=randomuser.me --reload ./scratch.js

[error]
Enter fullscreen mode Exit fullscreen mode

Okay. Up until now, we worked with modules, importing local files and remote files.
We played with fire by not versioning our dependencies and relied on the integrity check to save us from ourselves.
The Deno community also maintains module registries, similar to NPM. These registries enforce SemVer on all modules -- this is an essential when dealing with multiple dependencies.

We are going to visit two registries, but there are more.
First up is deno/x -- the Deno core team maintains this registry. It is simple to use you can leverage Github Webhook to publish a module.

Next up is Nest -- Nest publishes modules to the permaweb, meaning that they are permanent and immutable.
They do this through blockchain technology. Unlike deno/x, Nest uses a CLI tool to publish modules.

Using a module from one of these registries, you will notice that the path includes a version number that ensures that your import code is immutable.
It also means that you can -- in theory -- import different versions from
the same source.

In this episode, we experimented with Deno modules.
We also did an overview of the many ways you can publish a module within the Deno-verse.

On the next episode, I want to cover the many tools that come out of the box when installing Deno.

You have a linter, a formatter, a test runner, a bundler, an installer and, so much more; I might have to make it a three-parter.

Top comments (2)

Collapse
 
oganexon profile image
oganexon

Great article, keep it up!
You really explored all the aspects of deno imports and explained them very well

Collapse
 
redstuff profile image
Sveinung Tord Røsaker

Good stuff