Skip to content

[APPS-2791] Fix: set ssr:true when bundling backend functions - #460

Open
tyffical wants to merge 2 commits into
masterfrom
tiffany.trinh/apps-2791-ssr-true-backend-build-config
Open

[APPS-2791] Fix: set ssr:true when bundling backend functions#460
tyffical wants to merge 2 commits into
masterfrom
tiffany.trinh/apps-2791-ssr-true-backend-build-config

Conversation

@tyffical

@tyfficaltyffical commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Motivation

  • getBaseBackendBuildConfig never sets Vite's build.ssr option, so Vite defaults to a browser-target build for *.backend.ts bundling.
  • Any backend function that imports a real Node builtin module directly (e.g. import { randomBytes } from 'node:crypto') gets that import externalized to a broken __vite-browser-external:node:crypto stub instead of a working import — bundling fails with "randomBytes" is not exported by "__vite-browser-external:node:crypto".
  • Discovered as a side effect of prototyping local Node execution for backend functions (APPS-2792, a separate, related initiative) — not something that prototype introduces. This affects bundleBackendFunction() in the existing dev-server.ts today, for the current npm run dev cloud round-trip.
  • Update: setting ssr: true alone introduces a second, related issue — Vite's SSR build mode externalizes any real npm dependency it finds in node_modules by default too, not just Node builtins, on the assumption a server runtime can require() it at runtime. That assumption doesn't hold here: dev-server.ts writes bundles to a standalone temp file, and this repo's own packages/plugins/apps/src/backend/integration.test.ts (which builds and then directly import()s the real emitted bundle) caught this immediately — @datadog/apps-backend/user came back as an unresolvable bare import once ssr: true landed. Fixed by also setting ssr.noExternal: true, forcing every real dependency to stay inlined, matching the browser-mode behavior this config otherwise replaces.
  • APPS-2791

Changes

What changedFile
Added ssr: true to the build options — backend functions run server-side, never in a browserpackages/plugins/apps/src/vite/build-config.ts
Added ssr: { noExternal: true } — keeps real npm dependencies inlined under SSR mode instead of externalized as unresolvable bare importspackages/plugins/apps/src/vite/build-config.ts
Added a regression test bundling a fixture backend function that imports node:crypto, asserting the output contains a working import and no __vite-browser-external stubpackages/plugins/apps/src/vite/build-config.test.ts (new)

QA Instructions

yarn install

Confirm the new test fails without the fix (for reference — already verified locally, not something you need to re-check by reverting):

git stash -- packages/plugins/apps/src/vite/build-config.ts
BUILD_PLUGINS_ENV=test FORCE_COLOR=true JEST_CONFIG_TRANSPILE_ONLY=true VITE_CJS_IGNORE_WARNING=true NODE_OPTIONS="--experimental-vm-modules" yarn workspace @dd/tests exec jest --config jest.config.ts packages/plugins/apps/src/vite/build-config.test.ts
# Expected: FAILS with "randomBytes is not exported by __vite-browser-external:node:crypto" ✅ VERIFIED
git stash pop

Confirm the noExternal fix specifically — this is what the real @datadog/apps-backend integration test catches:

BUILD_PLUGINS_ENV=test FORCE_COLOR=true JEST_CONFIG_TRANSPILE_ONLY=true VITE_CJS_IGNORE_WARNING=true NODE_OPTIONS="--experimental-vm-modules" yarn workspace @dd/tests exec jest --config jest.config.ts packages/plugins/apps/src/backend/integration.test.ts
# Expected: 4 tests passing (fails with "Cannot find module '@datadog/apps-backend/user'" without ssr.noExternal) ✅ VERIFIED

Run the full suite:

BUILD_PLUGINS_ENV=test FORCE_COLOR=true JEST_CONFIG_TRANSPILE_ONLY=true VITE_CJS_IGNORE_WARNING=true NODE_OPTIONS="--experimental-vm-modules" yarn workspace @dd/tests exec jest --config jest.config.ts packages/plugins/apps
# Expected: 23 test suites, 285 tests, all passing ✅ VERIFIED
cd packages/plugins/apps && npx tsc -b tsconfig.client.json --emitDeclarationOnly && npx tsc --noEmit
# Expected: no output, clean exit ✅ VERIFIED

Manual QA — real scaffolded app, local dev server + real staging cloud round-trip

Automated tests cover the bundling output; this confirms the fix through the actual npm run dev path a customer would hit, both fixes at once (Node builtin + real npm dependency in the same function).

Setup: built and npm link'd this branch's @datadog/vite-plugin (yarn cli prepare-link + npm link per CONTRIBUTING.md's external-project linking flow), scaffolded a fresh app (npm create @datadog/apps@latest -- test-ssr-fix --template vite-react -y), linked the local build in, added:

// src/qaCheck.backend.tsimport{randomBytes}from'node:crypto';import{getExecutionUser}from'@datadog/apps-backend/user';exportasyncfunctionqaCheck(){constnonce=randomBytes(4).toString('hex');constuser=awaitgetExecutionUser();return{ nonce,userEmail: user.email,orgId: user.orgId};}

Local (npm run dev, POST /__dd/debugBundle): confirmed the emitted bundle contains a real working import { randomBytes } from 'node:crypto' (not __vite-browser-external) and @datadog/apps-backend/user's getExecutionUser fully inlined as real function bodies (not left as an unresolvable bare import) ✅ VERIFIED

Staging (dd-auth --domain dd.datad0g.com -- npm run dev, POST /__dd/executeAction — real preview-async cloud round-trip):

{"success":true,"result":{"data":{"nonce":"267d20a9","orgId":"bd4276fd-000e-11ea-a34b-3f3c8bba65b8","userEmail":"tiffany.trinh@datadoghq.com"}}}

Real nonce from node:crypto, real org/user identity from the real @datadog/apps-backend npm dependency, executed through the actual staging cloud infrastructure end-to-end ✅ VERIFIED

Blast Radius

  • Affects any customer *.backend.ts function that imports a Node builtin module directly (previously silently broken at build time; now works), and any function that imports a real npm dependency (previously would have been silently externalized to an unresolvable bare import once ssr: true landed; now stays inlined).
  • No config flag — this is a correctness fix to shared bundling config, not a new capability.
  • Verified no regressions across the full packages/plugins/apps test suite (285 tests) — this fix specifically closes a regression the suite's own integration.test.ts caught against a real, unmocked bundle.
  • Risk: low. ssr: true + ssr.noExternal: true together change Vite's target/externalization assumptions, but backend functions never ran in a browser and never relied on runtime node_modules resolution in the first place, so this only removes two incorrect defaults, and existing test coverage across the package (dev-server, virtual-entry, connection-collector, etc.) all still passes unchanged.

Documentation

@tyffical
tyfficalforce-pushed the tiffany.trinh/apps-2791-ssr-true-backend-build-config branch from 7d48e17 to 66b0c70CompareJuly 28, 2026 20:09
@tyffical
tyfficalforce-pushed the tiffany.trinh/apps-2791-ssr-true-backend-build-config branch from 66b0c70 to d1e8071CompareJuly 29, 2026 18:08
getBaseBackendBuildConfig never set Vite's build.ssr option, so Vite
defaulted to a browser-target build. Any *.backend.ts file importing a
real Node builtin module (e.g. node:crypto) got that import
externalized to a broken __vite-browser-external:* stub instead of a
working import, breaking the bundle at build time.
Backend functions run server-side, never in a browser, so ssr:true is
the correct target. Affects both the existing cloud round-trip and any
future local-execution path, since both share this config.
Co-Authored-By: Claude <noreply@anthropic.com>
@tyffical
tyfficalforce-pushed the tiffany.trinh/apps-2791-ssr-true-backend-build-config branch from d1e8071 to 454108cCompareAugust 21, 2026 14:57
@tyffical
tyffical requested a balanced review from CopilotAugust 21, 2026 16:23

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Friend, this PR configures backend functions as self-contained Vite SSR bundles, preserving Node built-in imports and inlining npm dependencies.

Changes:

  • Enables SSR mode with dependency inlining.
  • Adds a Node built-in regression test.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

FileDescription
packages/plugins/apps/src/vite/build-config.tsConfigures standalone SSR backend bundles.
packages/plugins/apps/src/vite/build-config.test.tsTests preservation of Node built-in imports.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment threadpackages/plugins/apps/src/vite/build-config.test.ts Outdated
Comment threadpackages/plugins/apps/src/vite/build-config.test.ts Outdated
Comment threadpackages/plugins/apps/src/vite/build-config.ts Outdated
@tyffical

Copy link
Copy Markdown
ContributorAuthor

@cursor review
@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit:454108c8b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The ssr.noExternal rationale described a data:-URL local-execution path
that was never actually implemented — dev-server.ts's dev path builds
in-memory and sends the code to the Datadog API, and the production path
writes standalone temp files that get uploaded, not required from disk.
Also: named the Date.now() seed instead of inlining it into the
getTempWorkingDir call, and clean up the temp working directory in a
finally block so repeated local/CI runs don't accumulate fixtures.
@tyffical
tyffical requested a balanced review from CopilotAugust 21, 2026 19:23
@tyffical

Copy link
Copy Markdown
ContributorAuthor

@codex review

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit:3cf0839b8c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@tyffical
tyffical marked this pull request as ready for review August 21, 2026 19:59
@tyffical
tyffical requested review from a team as code ownersAugust 21, 2026 19:59
@tyffical
tyffical requested review from drewwyatt and removed request for a teamAugust 21, 2026 19:59
Sign up for freeto 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.

2 participants

@tyffical