Deno Fundamentals
itWeb development
Deno Fundamentals
Deno is a JavaScript and TypeScript runtime. It executes programs outside a web browser. It also provides the tools that surround those programs.
You can run a TypeScript file, check its types, format it, lint it, and test it with one executable. Deno also reads familiar web APIs and npm packages. Its permission system denies most system input and output until you grant access.
This course gives you a working map of that system. You will learn where Deno fits, how a program moves from source code to execution, and where to make deliberate choices.
The four-part mental model
Think of Deno as four connected layers:
- Runtime: V8 executes JavaScript after Deno removes TypeScript syntax when needed.
- Toolchain: The command-line interface provides checking, formatting, linting, testing, task running, and more.
- Package resolver: Deno loads local modules plus dependencies from JSR, npm, and supported URLs.
- Permission boundary: Sensitive system access is denied by default and granted by category or resource.
These layers explain most day-to-day behavior. When a program fails, ask which layer owns the problem. A type error belongs to checking. An unresolved import belongs to package resolution. A denied network request belongs to permissions.
What Deno runs
Deno runs JavaScript and TypeScript directly. A small TypeScript program needs no separate compiler configuration:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("Deno"));
Save this as main.ts, then run it:
deno run main.ts
Execution and type checking are separate operations. During normal execution, Deno strips TypeScript syntax and sends JavaScript to V8. It does not reject type errors unless you request checking.
Use deno check main.ts for a check without execution. Use deno run --check main.ts when you want both operations. The built-in test runner type-checks by default.
This separation keeps the edit-run loop quick. It also means you must include a type-checking step in your editor or continuous integration process.
Web standards as the shared vocabulary
Deno implements many web platform APIs outside the browser. You work with familiar objects such as Request, Response, URL, fetch, streams, and web cryptography.
That shared vocabulary reduces the gap between browser and server code. It does not make every environment identical. Browser-only objects such as the document object model are not part of the default Deno runtime scope.
Deno also exposes runtime-specific capabilities through the Deno namespace. For example, Deno.readTextFile reads a text file and Deno.serve starts an HTTP server.
Permissions are part of execution
A Deno program cannot freely access sensitive system resources by default. File access, network access, environment variables, subprocesses, system information, and native libraries have permission controls.
Suppose a program fetches data from api.example.com. Running it without permission causes an error. Grant only the required host:
deno run --allow-net=api.example.com main.ts
Scoped grants express the program's expected operating boundary. A file processor might receive read access to one input directory and write access to one output directory.
Avoid reaching first for --allow-all. That flag removes the protection you gain from explicit permissions. Treat subprocess and native-library permissions with extra care because code beyond Deno's JavaScript sandbox can exercise operating-system privileges.
Permissions control runtime input and output. They do not prove that a dependency is safe or free of known vulnerabilities. Dependency review, lockfiles, and deno audit address different supply-chain risks.
Modules and dependencies
Deno uses ECMAScript modules. Local imports use relative paths. Third-party packages commonly use JSR or npm.
import { assertEquals } from "jsr:@std/assert";
import chalk from "npm:chalk@5";
import { add } from "./math.ts";
JSR is a JavaScript registry designed around modern modules and is the home of the Deno Standard Library. Deno can also load npm packages and provides compatibility for Node built-in APIs.
For a small script, an explicit package specifier can be enough. For an application, declare dependencies in deno.json or package.json. This centralizes versions and gives update tools a clear place to work.
Deno caches dependencies. A local node_modules directory is not required for many Deno-first projects. Some frameworks, native add-ons, and Node-oriented tools expect one. Deno supports configuration modes for those cases.
Compatibility is broad, but it is not a reason to ignore boundaries. A package may assume Node-specific file layout, lifecycle scripts, or native behavior. Test the actual package and deployment target.
Configuration and tasks
Deno recognizes deno.json, deno.jsonc, and package.json. Both configuration families are optional.
Use package.json when an existing Node project already defines dependencies and scripts. Add deno.json when you need Deno-specific settings such as formatter rules, linter rules, import mappings, compiler options, or tasks.
A compact deno.json might look like this:
{
"tasks": {
"check": "deno fmt --check && deno lint && deno check main.ts",
"test": "deno test"
},
"imports": {
"@std/assert": "jsr:@std/assert@^1.0.0"
}
}
Run a task with deno task check. Tasks give a project stable names for repeatable commands. They also keep required permissions visible when you include permission flags in the task definition.
Built-in development tools
Deno's command-line tools cover common feedback loops:
deno fmtformats supported source files.deno lintchecks code against lint rules.deno checktype-checks a module graph.deno testdiscovers and runs tests.deno taskruns project commands.deno infoexplains modules, dependencies, and cache locations.deno docdisplays documentation for modules and symbols.
Built-in tools reduce project setup and make conventions easier to share. They do not remove architectural decisions. You still decide module boundaries, dependency policy, test scope, deployment shape, and permission grants.
A small HTTP service
Deno includes an HTTP server API that uses web-standard request and response objects:
Deno.serve((_request) => {
return new Response("Hello from Deno");
});
Run this server with network permission:
deno run --allow-net server.ts
The handler runs for each request and returns a response or a promise of one. This is enough for a small service. Larger applications usually add routing, validation, logging, persistence, and graceful shutdown.
Deno is also useful outside HTTP services. Common uses include command-line tools, automation, test tooling, build scripts, scheduled jobs, and applications that reuse npm packages.
Where Deno fits
Deno is a strong candidate when you value direct TypeScript execution, web-standard APIs, integrated tooling, and explicit runtime permissions. It can also help teams run existing Node projects or adopt a new runtime incrementally.
It may not be the best first choice when a required dependency depends on unsupported Node behavior, when an established platform only supports another runtime, or when a team cannot absorb a runtime change. Compatibility claims never replace a proof on your workload.
The practical evaluation is small and concrete. Run representative code. Exercise required packages. Check permissions. Test production packaging and observability. Measure the behavior that matters to your service.
A path forward
Start with a local TypeScript file and the core commands. Then add one dependency and centralize it in configuration. Next, write a test and define project tasks. Build a small HTTP or command-line program with narrowly scoped permissions.
After that foundation, study Node compatibility, package and workspace management, deployment, observability, and supply-chain controls. The official reference should remain your source for exact flags and current runtime behavior.
