Files, Modules, and Dependencies
Learning Objectives
- You can read files and organize a CLI program into modules.
- You know how
deno.jsonis used in a small project. - You understand how to add dependencies from
jsr:and how environment variables fit into local configuration.
File input in Deno
Command-line tools often work with local files. Deno provides built-in support for this.
To read a single text file:
const text = await Deno.readTextFile("./data/hello.txt");
console.log(text);
Because the program reads from the local file system, it must be run with read permission:
$ deno run --allow-read app.js
You can also run the program without the permission flag. Then, when Deno attempts to open a file, it will ask for the permission.
Splitting a project into modules
Even a small CLI program becomes easier to maintain when responsibilities are separated.
Figure 1 shows a simple project layout that we will use in the text-processing tutorial at the end of this part.
The structure can make the program easier to explain. If a developer can say “this file reads inputs, this file performs the processing, and this file formats the output”, the project is already much easier to understand than a single long script with all three concerns mixed together.
deno.json
Deno projects often include a deno.json file for configuration and dependency declarations.
For example:
{
"imports": {
"@std/path": "jsr:@std/path@1.0.8"
}
}
With this configuration in place, the program can import the dependency using the short name:
import * as path from "@std/path";
This file often becomes the place where small project-wide decisions are collected. That makes the rest of the code cleaner, because the imports and runtime settings do not need to be repeated in many places.
Adding dependencies
A dependency can be added with deno add.
$ deno add @std/path@1.0.8
This adds the dependency to deno.json and also updates a lock file deno.lock that keeps track of the explicit versions. The jsr prefix refers to the JavaScript Registry.
Environment variables
Programs often need small pieces of configuration that should not be hard-coded. A later LLM client, for example, may need the API URL, the model name, and the API key.
In Deno, environment variables can be read like this:
const model = Deno.env.get("LLM_MODEL") ?? "default-model";
If the program reads environment variables, run it with --allow-env.
This becomes especially useful when the same program should work in different settings. For example, a developer might want to test against a different endpoint in development than in production. Environment variables make that possible without editing the source file.
A practical structure rule
When a CLI program grows beyond a single page of code, ask whether it would be clearer if one file handled I/O, one file handled the main processing logic, and one file handled formatting or output generation.
That structure is often enough to keep a small project readable. It also makes later testing easier, because the main logic can often be tested separately from the file system and the terminal output. You can later on add new files with new responsibilities, or divide the existing files further, but this simple structure is a good starting point.