npm package management for Elixir — resolve, fetch, cache, and link npm packages from Mix without requiring Node.js for installation.
mix npm.install lodash
mix npm.exec eslint .npm_ex reads package.json, resolves npm semver with PubGrub, writes npm.lock, and links packages into node_modules/.
Elixir projects increasingly need JavaScript packages for assets, formatters, linters, browser libraries, and runtime integrations. npm_ex keeps that workflow inside Mix:
- no
npm installstep required for dependency resolution or linking - reproducible installs through
npm.lock - global package cache in
~/.npm_ex/cache/ - npm registry auth, mirrors, scoped registries, peer/deprecation warnings
- CI-friendly Mix tasks for install, verify, audit, outdated, tree, and exec
def deps do
[{:npm, "~> 0.7.0"}]
endmix npm.init
mix npm.install lodash# Install and maintain dependencies
mix npm.install
mix npm.install lodash@^4.0
mix npm.install eslint --save-dev
mix npm.update
mix npm.remove lodash
# CI / reproducibility
mix npm.install --frozen
mix npm.ci
mix npm.verify
# Inspect dependency state
mix npm.list
mix npm.tree
mix npm.why accepts
mix npm.outdated
# Run scripts and binaries
mix npm.run build
mix npm.exec eslint .
# Registry, cache, and config
mix npm.info express
mix npm.search react
mix npm.cache status
mix npm.config- Read
package.jsondependencies, dev dependencies, optional dependencies, and overrides. - Resolve the full dependency tree using hex_solver and npm_semver.
- Fetch registry packuments and tarballs with integrity verification.
- Store package contents in the global cache.
- Link packages into
node_modules/and writenpm.lock.
npm_ex uses its own npm.lock because it is not npm. package.json remains the shared manifest; npm.lock records npm_ex's resolved dependency graph and security policy.
npm_ex is intentionally conservative around install-time code execution:
- package lifecycle hooks are not executed automatically
- packages declaring
preinstall,install,postinstall, orprepareare installed but reported as warnings - tarball paths are validated before extraction to prevent cache escapes
- transitive git, URL, GitHub shorthand, and
file:dependencies are blocked by default - direct exotic dependencies require an explicit
exotic_depsallowlist entry - registry origins and redirects are policy checked
- newly created packages and freshly published versions can warn during install
This blocks common install-time credential stealers that rely on postinstall hooks reading files like .env and exfiltrating secrets during dependency installation.
mix npm.audit supports npm vulnerability checks and OSV/OpenSSF malicious-package intelligence:
# npm registry vulnerability audit
mix npm.audit
# Strict online OSV malicious-package gate
mix npm.audit --osv
# Refresh the shared local malicious-package cache for the current lockfile
mix npm.audit --osv --write-cache --policy warn
# Deterministic offline gate using the shared cache or configured DB
mix npm.audit --compromised--write-cache merges matching OSV advisories into ~/.npm_ex/security/compromised_packages.json by default. mix npm.audit --osv fails closed when OSV cannot be queried; mix npm.audit --compromised is offline and deterministic.
OpenSSF/OSV is the default-compatible open data source. Socket, Snyk, and Phylum provide valuable proprietary intelligence or install-time firewall workflows; they fit best as external scanners/proxies or future optional integrations rather than default npm_ex install dependencies.
Most projects only need the defaults. Use mix npm.config to inspect effective settings.
Common environment variables:
NPM_REGISTRY,NPM_TOKEN,NPM_MIRRORNPM_EX_CACHE_DIR,NPM_INSTALL_DIRNPM_EX_BLOCK_EXOTIC_SUBDEPS,NPM_EX_EXOTIC_DEPSNPM_EX_ALLOWED_REGISTRIES,NPM_EX_ALLOW_REGISTRY_REDIRECTSNPM_EX_PACKAGE_AGE_WARNING_DAYS,NPM_EX_VERSION_AGE_WARNING_DAYSNPM_EX_COMPROMISED_DB_PATH,NPM_EX_COMPROMISED_POLICY
Elixir application config is also supported:
config :npm,
registry: "https://registry.npmjs.org",
token: System.get_env("NPM_TOKEN"),
cache_dir: Path.expand("~/.npm_ex"),
block_exotic_subdeps: true,
exotic_deps: [],
allowed_registries: ["https://registry.npmjs.org"],
allow_registry_redirects: false,
package_age_warning_days: 7,
version_age_warning_days: 3,
compromised_db_path: Path.expand("~/.npm_ex/security/compromised_packages.json"),
compromised_policy: :errorThe main public API is NPM. Supporting modules are grouped by domain: NPM.Package.*, NPM.Dependency.*, NPM.Lockfile.*, NPM.Security.*, NPM.Registry.*, NPM.Config.*, NPM.Install.*, NPM.Node.*, NPM.NodeModules.*, and NPM.Diagnostics.*.
See CHANGELOG.md for the 0.7 migration map from older pre-namespace module names.
Full guides and API documentation are available on HexDocs:
- Getting Started
- Dependency Workflows
- CI and Reproducibility
- Supply-Chain Safety
- Malicious Package Audits
- Configuration
- CLI and configuration cheatsheets
MIT © 2026 Danila Poyarkov