Skip to content

feat: NodeJS support#761

Open
mconflitti-pbc wants to merge 12 commits intomainfrom
nodejs-support
Open

feat: NodeJS support#761
mconflitti-pbc wants to merge 12 commits intomainfrom
nodejs-support

Conversation

@mconflitti-pbc
Copy link
Copy Markdown
Contributor

@mconflitti-pbc mconflitti-pbc commented Mar 20, 2026

Intent

Add client-side support for deploying Node.js API applications (Express, Fastify, etc.) to Posit Connect via rsconnect deploy nodejs. This is the rsconnect-python counterpart to the Connect server-side Node.js runtime support that is in-progress.

Type of Change

  • Bug Fix
  • New Feature
  • Breaking Change

Approach

Follows the existing Python API deployment patterns throughout:

App Mode — Added NODE_API (ordinal 20, name "node-api") to AppModes in models.py, matching Connect's NodeAPIMode.

Environment Detection (environment_node.py) — New NodeEnvironment class that:

  • Detects Node.js and npm versions via subprocess
  • Reads and parses package.json for dependency metadata
  • Warns when package-lock.json is missing (not required, matching Python's precedent with requirements.txt)

Entry Point Detection (bundle.py) — Auto-detects entry point from:

  1. package.json main field
  2. package.json scripts.start (parses node <file> pattern)
  3. Common filenames: app.js, index.js, server.js, main.js (+ .ts variants)

Bundle Creation (bundle.py) — make_nodejs_manifest() and make_nodejs_bundle() produce manifests matching Connect's expected format:

  • node section with version and package_manager
  • packages section with npm dependency metadata
  • node_modules/ always excluded from bundles

CLI Command (main.py) — rsconnect deploy nodejs with options:

  • --entrypoint / -e — JS/TS entry point file
  • --node — path to Node.js executable
  • --exclude / -x — glob patterns
  • --disable-env-management-node
  • Standard server, content, and cloud/shinyapps args

Key decisions

  • package-lock.json included when present, not required — Connect's build_environment.js handles both paths (npm ci with lock file, npm install without). Matches Python precedent.
  • devDependencies excluded from manifest packages section — only production dependencies are declared.
  • TypeScript support via Node.js 24+ native type stripping — no build step needed, just deploy .ts files directly.

Automated Tests

165 tests pass across 3 test files:

  • tests/test_environment_node.py (17 tests) — Version detection, package.json parsing, error handling, lock file detection, custom executable
  • tests/test_bundle.py (50 new tests) — Manifest structure, package metadata, node_modules exclusion, lock file inclusion, entry point auto-detection (JS + TS), bundle contents
  • tests/test_main.py (5 new tests) — CLI help, no-args, missing directory, missing package.json, bad entrypoint

Test fixtures:

  • tests/testdata/node-express/ — JavaScript Express API
  • tests/testdata/node-ts-express/ — TypeScript Express API

Directions for Reviewers

  1. CLI help: rsconnect deploy nodejs --help — verify options and description
  2. JS deploy (requires Connect with Node.js enabled):
    rsconnect deploy nodejs -s https://connect.example.com -k $API_KEY tests/testdata/node-express/
  3. TS deploy:
    rsconnect deploy nodejs -s https://connect.example.com -k $API_KEY tests/testdata/node-ts-express/
  4. Auto-detection: Run without --entrypoint — should detect app.js / app.ts from package.json main
  5. Error cases: Deploy a directory without package.json — should get clear error message

Manual verification with local connect instance

(nodejs-support) ➜  nodejs-support git:(nodejs-support) ✗ python -m rsconnect.main deploy nodejs -s http://localhost:4939 -k ***** tests/testdata/node-express/
    Warning: No package-lock.json found. Deployments without a lock file may not be reproducible.
Validating server...    [OK]
Validating app mode...  [OK]
Making bundle ...       [OK]
Deploying bundle ...    [OK]
Saving deployed information...  [OK]
Building Node.js API...
Bundle created with Node.js version 22.17.0 is compatible with environment Local with Node.js version 22.22.1 from /opt/node/22.22.1/bin/node
Bundle requested Node.js version 22.17.0; using /opt/node/22.22.1/bin/node from Local which has version 22.22.1
2026/03/20 17:26:15.238762177 [connect-session] Connect Session v2026.04.0-dev+45-g6d703cf8b5
2026/03/20 17:26:15.240147968 [connect-session] Received trace context: traceID=123bc2d04961ac949c37c6dd81aef9c9
2026/03/20 17:26:15.240206927 [connect-session] Content GUID: f3b5b516-38b4-4095-b1cd-4489f2374a3f
2026/03/20 17:26:15.240269968 [connect-session] Content ID: 12
2026/03/20 17:26:15.240324718 [connect-session] Bundle ID: 12
2026/03/20 17:26:15.240387010 [connect-session] Job Key: n0S0AMGoA0LeS327
2026/03/20 17:26:15.240487468 [connect-session] Warning: Failed to create Connect client: "CONNECT_SERVER" is missing or empty
2026/03/20 17:26:15.240581510 [connect-session] Warning: Workload identities are disabled
Job started
Determining session server location ...
Connecting to session server http://127.0.0.1:38495 ...
Connected to session server http://127.0.0.1:38495
2026/03/20 17:26:15.300477718 >>> Building Node.js environment
2026/03/20 17:26:15.300546468 >>> Node.js version: v22.22.1
2026/03/20 17:26:15.450611552 >>> npm version: 11.9.0
2026/03/20 17:26:15.450820302 >>> No package-lock.json found; using npm install
2026/03/20 17:26:15.450872635 >>> Running: npm install --omit=dev
2026/03/20 17:26:18.306523720
2026/03/20 17:26:18.306534261 added 68 packages, and audited 69 packages in 3s
2026/03/20 17:26:18.306643220
2026/03/20 17:26:18.306676178 15 packages are looking for funding
2026/03/20 17:26:18.306719886   run `npm fund` for details
2026/03/20 17:26:18.307156845
2026/03/20 17:26:18.307160678 found 0 vulnerabilities
2026/03/20 17:26:18.307800553 npm notice
2026/03/20 17:26:18.307804720 npm notice New minor version of npm available! 11.9.0 -> 11.12.0
2026/03/20 17:26:18.307875845 npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.12.0
2026/03/20 17:26:18.308028928 npm notice To update run: npm install -g npm@11.12.0
2026/03/20 17:26:18.308029553 npm notice
2026/03/20 17:26:18.371912428 >>> Node.js environment build complete
Completed Node.js build using Local against Node.js version: '22.22.1'
Job completed
Stopped session pings to http://127.0.0.1:38495
Launching Node.js API...
Deployment completed successfully.
         Dashboard content URL: http://localhost:4939/connect/#/apps/f3b5b516-38b4-4095-b1cd-4489f2374a3f
         Direct content URL: http://localhost:4939/content/f3b5b516-38b4-4095-b1cd-4489f2374a3f/
Verifying deployed content...   [OK]

Checklist

  • I have updated CHANGELOG.md to cover notable changes.
  • I have updated all related GitHub issues to reflect their current state.
  • I have run the rsconnect-python-tests-at-night workflow in Connect against this feature branch.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 20, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://posit-dev.github.io/rsconnect-python/pr-preview/pr-761/

Built to branch gh-pages at 2026-03-20 19:24 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Add NODE_API app mode (ordinal 20, name "node-api") to AppModes registry
and add TypedDicts for the Node.js manifest section (ManifestDataNode,
ManifestDataNodePackageManager, ManifestDataPackage) matching the format
expected by Connect PR #37892.
Add NodeEnvironment class that detects Node.js/npm versions, reads
package.json, parses dependencies into manifest packages format, and
warns when package-lock.json is missing. Includes tests and test fixtures.
…hase 3)

Add node_modules/ to the global directory ignore list. Add
get_default_node_entrypoint() that checks package.json main field,
scripts.start, then common filenames (app.js, index.js, etc.).
Add validate_node_entry_point() for user-specified entry point validation.
Add make_nodejs_manifest() and make_nodejs_bundle() functions that produce
manifests matching the format expected by Connect (node section with version
and package_manager, packages section with npm dependency metadata).
Fix circular import between bundle.py and environment_node.py using
TYPE_CHECKING guard.
Add deploy nodejs subcommand with --entrypoint, --exclude, --node,
--image, and --disable-env-management-node options. Auto-detects
entry point from package.json when not specified.
Add tests for make_nodejs_manifest(), make_nodejs_bundle(),
get_default_node_entrypoint(), and validate_node_entry_point().
Covers manifest structure, package metadata, node_modules exclusion,
lock file inclusion, entry point auto-detection, and error cases.
Add TestDeployNodeJS class with tests for help output, no-args behavior,
missing directory, missing package.json, and bad entrypoint error cases.
Document the new rsconnect deploy nodejs command in the unreleased
section of the changelog.
Add node-ts-express test fixture with TypeScript entry point (app.ts)
and @types/express devDependency. Add TestNodeJSTypeScriptBundle tests
covering TS manifest structure, bundle contents, entry point detection,
and devDependencies exclusion from packages section.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 20, 2026

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
5617 4325 77% 0% 🟢

New Files

File Coverage Status
rsconnect/environment_node.py 100% 🟢
TOTAL 100% 🟢

Modified Files

File Coverage Status
rsconnect/bundle.py 81% 🟢
rsconnect/main.py 69% 🟢
rsconnect/models.py 92% 🟢
TOTAL 81% 🟢

updated for commit: 59a91f3 by action🐍

Add rsconnect write-manifest nodejs command following the Python
write-manifest pattern, with --overwrite, --entrypoint, --node,
--image, and --disable-env-management-node options.

Remove test_no_args_shows_help test that failed on py3.10+ due to
Click version differences in no_args_is_help exit codes. No Python
deploy command tests this behavior either.
@mconflitti-pbc mconflitti-pbc marked this pull request as ready for review March 20, 2026 18:49
Connect doesn't consume a `packages` section for Node.js deployments —
it runs `npm install` against package.json directly. Remove the
ManifestDataPackage types, _parse_packages(), and related tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant