package.json does not configure Deno itself
A package.json configures your project's dependencies and scripts, but it does not configure Deno itself. Deno-specific settings such as the formatter, linter, TypeScript compiler options, and lockfile behavior live only in deno.json.
Deno reads dependencies from both package.json and deno.json
When both package.json and deno.json files are present, Deno reads dependencies from each and takes its own configuration from deno.json.
deno.json purpose
deno.json is where you configure Deno itself: tasks, dependencies, and tools like the TypeScript compiler, linter, and formatter. It is optional.
Minimal deno.json example
A minimal deno.json file looks like this:
```json
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@std/assert": "jsr:@std/assert@^1"
},
"fmt": {
"lineWidth": 100
}
}
```
deno.json supports .jsonc extension
deno.json supports both .json and .jsonc (JSON with comments) extensions. With deno.jsonc you can add comments and trailing commas.
deno.json auto-discovery
Deno automatically detects a deno.json or deno.jsonc file in your current working directory or any parent directory, which is what makes a project's settings apply to every file under it.
deno.json config flag
Use the --config flag to point Deno at a different configuration file instead of the auto-discovered one.
deno.json workspace support
In a monorepo, a root deno.json can define a workspace whose members each carry their own deno.json.
deno.json configurable fields
A deno.json file can configure the following: dependencies and import maps, tasks, linting and formatting, lockfile and node_modules directory, TypeScript compiler options, unstable feature flags, include and exclude patterns, exports, permissions, compile options, and minimum dependency age.
Deno reads two configuration files
Deno reads two configuration files: Node's package.json and Deno's own deno.json. Both are first-class and both are optional, so Deno works with either one or both together.
When to use package.json vs deno.json
Use package.json for dependencies and scripts. Deno reads it directly, so most Node.js projects run with no changes and you do not need a deno.json at all. Add deno.json when you want to configure Deno's own tooling, such as the formatter, linter, TypeScript compiler, or tasks.
Deno package.json support
Deno has first-class package.json support. You can point Deno at an existing Node.js project and it resolves npm dependencies from package.json and runs the project's scripts with deno task, with no deno.json and no conversion step.
.npmrc email field for legacy auth
For Deno 2.8+, some legacy on-prem registries require an email field alongside the auth token in .npmrc: //registry.mycompany.com/:_auth=secretToken and //registry.mycompany.com/:email=ci@mycompany.com.
.npmrc min-release-age supply-chain guard
Set min-release-age in .npmrc to refuse installing package versions younger than the configured age. Since Deno 2.9, a 24-hour minimum is applied by default. Control via .npmrc min-release-age, CLI flag --minimum-dependency-age, deno.json minimumDependencyAge field, or NPM_CONFIG_MIN_RELEASE_AGE env var. Set to 0 to turn off.
.npmrc trust-policy for publishing-trust level
Set trust-policy=no-downgrade in .npmrc to refuse resolving package versions whose publishing-trust level is weaker than the one recorded in lockfile. Off by default. Weak trust levels include no trusted publishing, no provenance, or staged publishing.
Handling file: and link: dependencies in npm packages
Starting in Deno 2.8, file: and link: entries in package.json dependencies of published npm packages are silently skipped during npm metadata resolution. Packages carrying stray local-path dependencies install cleanly instead of failing with 'Invalid version requirement' error.
NPM_CONFIG_REGISTRY environment variable precedence
The NPM_CONFIG_REGISTRY env var overrides the registry set in .npmrc, matching npm's precedence. Useful in CI when redirecting installs without editing checked-in .npmrc.
package.json support in Deno
Deno understands package.json. Dependencies declared in package.json are installed by deno install and importable by bare specifier. Scripts run via deno task like npm run. Fields like type are respected when resolving modules. Engines constraints are checked and Deno prints a warning if the node or deno version requirement is not satisfied.
Running Node project with deno install and permissions
To run an existing Node project, execute deno install to read package.json and create a node_modules directory, then run deno run -R -E main.js. The -R flag grants read access and -E grants environment access, which resolving node_modules typically needs.
Node executable shim when node not on PATH
When no real node binary is found on PATH, Deno places a node executable in its cache directory and prepends that directory to the PATH of processes it starts. Tools that spawn node then reach Deno, which translates Node arguments and runs as if invoked with deno node. This is best-effort and only activates when a real node is not on PATH. Set DENO_DISABLE_NODE_SHIM=1 to turn it off.
CommonJS file detection in Deno
Deno treats .cjs files as CommonJS without consulting package.json. For .js, .jsx, .ts, and .tsx files, Deno loads them as CommonJS if there is a package.json file with "type": "commonjs" next to the file or up in the directory tree. Use --unstable-detect-cjs flag in Deno >= 2.1.2 to enable module content analysis (except when package.json has "type": "module").
node_modules modes: none, auto, manual
Deno supports three node_modules modes. The default none mode does not create a node_modules directory and is recommended for most Deno projects. Auto mode creates a local node_modules directory automatically via --node-modules-dir=auto flag or "nodeModulesDir": "auto" in deno.json. Manual mode requires an explicit deno install step and is the default for projects with package.json.
Enabling auto node_modules mode
Use --node-modules-dir=auto flag on a per-command basis (deno run --node-modules-dir=auto main.ts) or set "nodeModulesDir": "auto" in deno.json. Auto mode automatically installs dependencies into the global cache and creates a local node_modules directory in the project root. Recommended for projects using bundlers or with npm dependencies that have postinstall scripts.
node_modules layouts: isolated vs hoisted
Deno supports two node_modules layouts. The default isolated layout installs each package into a content-addressed .deno/ directory and exposes it through a symlink, similar to pnpm. Hoisted layout places the most-depended-upon version of each package at the top of node_modules/, matching npm and Yarn classic. Enable hoisted with "nodeModulesLinker": "hoisted" in deno.json (requires "nodeModulesDir": "manual"). Stick with isolated mode unless a tool requires hoisted layout.
Running npm package build scripts in Deno
Many npm native addons rely on lifecycle scripts like postinstall to build or download their native binding. Deno does not run these scripts by default for security reasons. Use deno install --allow-scripts=npm:package-name to allow specific packages' scripts, or run deno approve-scripts interactively.
Node type definitions in Deno 2.8+
Starting in Deno 2.8, deno check and the LSP include lib.node in every type-check by default, so Node ambient types like Buffer, NodeJS.Timeout, and process resolve without configuration. The bundled lib.node tracks the major version of @types/node matching the Node release Deno reports in process.versions.node. Pin a specific @types/node version by adding it as an explicit dependency in deno.json imports.
Private registries with .npmrc in Deno
Configure .npmrc file in project root or $HOME to point to private registries. Example: @mycompany:registry=http://mycompany.com:8111/ and //mycompany.com:8111/:_authToken=secretToken. Then specify the import path in deno.json or package.json: "@mycompany/package": "npm:@mycompany/package@1.0.0".
.npmrc mutual-TLS authentication
For Deno 2.8+, .npmrc supports mutual-TLS authentication with certfile and keyfile pointing at PEM files: //registry.mycompany.com/:certfile=/etc/deno/client.crt and //registry.mycompany.com/:keyfile=/etc/deno/client.key.
Manual span creation with startSpan
Use tracer.startSpan(spanName, options) to manually create a span without setting it as the active span. This returns a span object that must be manually managed. Unlike startActiveSpan, this span will not automatically be used as a parent for spans created later or console.log calls. A span can be manually set as active using the context propagation API.
Enable OpenTelemetry with OTEL_DENO environment variable
To enable OpenTelemetry integration in Deno, set the environment variable OTEL_DENO=true when running a script. For example: OTEL_DENO=true deno run my_script.ts
Default OpenTelemetry endpoint and protocol
By default, OpenTelemetry data is exported to localhost:4318 using the http/protobuf protocol when OTEL_DENO=true is set.
Console exporter for OpenTelemetry debugging
To print OpenTelemetry spans, logs, and metrics directly to stderr in human-readable format without setting up a collector, use: OTEL_DENO=true OTEL_EXPORTER_OTLP_PROTOCOL=console deno run my_script.ts
OTEL_EXPORTER_OTLP_PROTOCOL supported values
The OTEL_EXPORTER_OTLP_PROTOCOL environment variable supports: http/protobuf (default), http/json, grpc (available in Deno 2.8+), and console.
OpenTelemetry automatic instrumentation scope name
Automatically collected Deno runtime observability data is exported in the built-in instrumentation scope named 'deno'. The version of this scope corresponds to the Deno runtime version (e.g., deno:2.1.4).
Automatic console log collection in OpenTelemetry
Any logs created with console.* methods such as console.log and console.error are automatically collected and exported. Logs raised from JavaScript code are exported with the relevant span context if they occur inside an active span.
OTEL_DENO_CONSOLE configuration options
The OTEL_DENO_CONSOLE environment variable configures console auto instrumentation with these options: 'capture' (default) - logs emitted to stdout/stderr and exported with OpenTelemetry; 'replace' - logs only exported with OpenTelemetry, not to stdout/stderr; 'ignore' - logs only to stdout/stderr, not exported with OpenTelemetry.
Get OpenTelemetry tracer in Deno
To create a tracer in Deno, import trace from npm:@opentelemetry/api@1 and call trace.getTracer(name, version). For libraries, name should be the library name; for applications, name should be the application name.
Create and manage spans with startActiveSpan
Use tracer.startActiveSpan(spanName, callback) to create a new span. The created span becomes the active span inside the callback. You must manually call span.end() in a finally block to ensure the span ends even if an error occurs. The method returns the return value of the callback function.
Record exceptions in OpenTelemetry spans
Use span.recordException(error) to record an exception that occurred during a span's lifetime. recordException creates an event with the exception stack trace and name and attaches it to the span. Note: recordException does not set the span status to ERROR; you must do that manually with span.setStatus().
Add attributes to OpenTelemetry spans
Span attributes are key-value pairs representing structured metadata. Use span.setAttribute(key, value) or span.setAttributes(obj) to add attributes. Values can be strings, numbers (floats), bigints (clamped to u64), booleans, or arrays of these types. Other types are ignored.
Update span name in OpenTelemetry
The name of a span can be updated using span.updateName(newName) on the span object.
Add events to OpenTelemetry spans
Use span.addEvent(name, attributes, timestamp) to add an event to a span. Events are points in time associated with the span. Attributes are optional and work like span attributes. Timestamp is optional and defaults to the current time.
OpenTelemetry span options
Both tracer.startActiveSpan and tracer.startSpan accept an optional options bag with properties: kind (SpanKind.CLIENT, SERVER, PRODUCER, CONSUMER, or INTERNAL; defaults to INTERNAL), startTime (Date object or number in milliseconds since Unix epoch), attributes (object of attributes to add), links (array of links to add), and root (boolean indicating if span should not have a parent).
Get OpenTelemetry meter in Deno
To create a meter in Deno, import metrics from npm:@opentelemetry/api@1 and call metrics.getMeter(name, version). For libraries, name should be the library name; for applications, name should be the application name.
OpenTelemetry counter instrument
A counter is a monotonically increasing value that can only be positive. Counters are used for values that are always increasing, such as the number of requests handled. Create with meter.createCounter(name, options) and record values with counter.add(value, attributes).
OpenTelemetry UpDownCounter instrument
An UpDownCounter is a value that can both increase and decrease. UpDownCounters are used for values that can increase and decrease, such as the number of active connections or requests in progress. Create with meter.createUpDownCounter(name, options) and record values with upDownCounter.add(value, attributes).
OpenTelemetry Gauge instrument
A Gauge is a value that can be set to any value. Gauges are used for values that do not accumulate over time but rather have a specific value at any given time, such as current temperature. Create with meter.createGauge(name, options) and record values with gauge.record(value, attributes).
OpenTelemetry Histogram instrument
A Histogram is a value recorded as a distribution of values, used for calculating percentiles, averages, and other statistics. Histograms have predefined bucket boundaries. Default boundaries are [0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 750.0, 1000.0, 2500.0, 5000.0, 7500.0, 10000.0]. Create with meter.createHistogram(name, options) and record values with histogram.record(value, attributes).
OpenTelemetry observable instruments
Observable instruments (ObservableCounter, ObservableUpDownCounter, ObservableGauge) do not have synchronous recording methods. Instead they return a callback that is called when the OpenTelemetry SDK is ready to record a value, such as just before exporting. Create with meter.createObservableCounter/UpDownCounter/Gauge() and add callbacks with instrument.addCallback((res) => res.observe(value, attributes)).
OpenTelemetry context propagation with AsyncContext
Context propagation in Deno uses AsyncContext rules (TC39 proposal). When a new asynchronous task is started, the current context is saved. Other code can execute concurrently in a different context. When the async task completes, the saved context is restored. This makes async context behave like a global variable scoped to the current async task, automatically copied to new async tasks started from it.
Get and manipulate OpenTelemetry context
Import context from npm:@opentelemetry/api@1. Use context.active() to get the currently active context. Use context.setValue(key, value) to create a new context with a value added. Use context.with(newContext, callback) to run a function in a new context.
Set span as active context in OpenTelemetry
To run a function in the context of a specific span, use trace.setSpan(context.active(), span) to create a context with the span, then run the function in that context with context.with(contextWithSpan, callback). Remember to call span.end() when finished.
OTEL_SERVICE_NAME environment variable
The OTEL_SERVICE_NAME environment variable sets the service.name attribute for telemetry data. If not set, it defaults to '<unknown_service>'.
OpenTelemetry resource attributes configuration
Resource attributes are configured using the OTEL_RESOURCE_ATTRIBUTES environment variable. Automatically set attributes include: service.name (defaults to '<unknown_service>' if OTEL_SERVICE_NAME not set), process.runtime.name (deno), process.runtime.version (Deno version), telemetry.sdk.name (deno-opentelemetry), telemetry.sdk.language (deno-rust), and telemetry.sdk.version (Deno version plus opentelemetry Rust crate version).
OTEL_PROPAGATORS environment variable
Propagators are configured using OTEL_PROPAGATORS. Default value is 'tracecontext,baggage'. Multiple propagators can be specified by separating with commas. Currently supported propagators are: tracecontext (W3C Trace Context propagation format) and baggage (W3C Baggage propagation format).
OTEL_TRACES_SAMPLER environment variable
Trace sampling is configured with OTEL_TRACES_SAMPLER. Supported values are: always_on (default - sample every trace), always_off (sample no traces), traceidratio (sample fraction of traces based on trace ID), parentbased_always_on (respect parent when present, otherwise always_on), parentbased_always_off (respect parent when present, otherwise always_off), and parentbased_traceidratio (respect parent when present, otherwise traceidratio).
OTEL_TRACES_SAMPLER_ARG environment variable
For ratio-based samplers (traceidratio, parentbased_traceidratio), OTEL_TRACES_SAMPLER_ARG sets the sampling probability as a number between 0 and 1. Defaults to 1.0. Example: OTEL_TRACES_SAMPLER=traceidratio OTEL_TRACES_SAMPLER_ARG=0.1
OTEL_METRIC_EXPORT_INTERVAL environment variable
Metric collection frequency is configured using OTEL_METRIC_EXPORT_INTERVAL. The default value is 60000 milliseconds (60 seconds).
Extract and inject OpenTelemetry context with propagators
Use propagation.extract(context, carrier) to extract context from incoming carrier (like HTTP headers). Use propagation.inject(context, carrier) to inject context into outgoing carrier. These are accessed via the propagation API from npm:@opentelemetry/api@1.
OpenTelemetry configuration with OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_PROTOCOL
The endpoint and protocol for the OTLP exporter can be configured using OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_PROTOCOL. Default endpoint is localhost:4318. Default protocol is http/protobuf. Supported protocols are: http/protobuf, http/json, grpc (Deno 2.8+), and console.