Skip to content

Remove node-fetch and form-data from cli-kit - #8287

Draft
isaacroldan wants to merge 2 commits into
mainfrom
isaac/remove-node-fetch-form-data
Draft

Remove node-fetch and form-data from cli-kit#8287
isaacroldan wants to merge 2 commits into
mainfrom
isaac/remove-node-fetch-form-data

Conversation

@isaacroldan

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Part of the effort to reduce external dependencies in the CLI. node-fetch (plus fetch-blob, formdata-polyfill, data-uri-to-buffer) and form-data (plus asynckit, combined-stream, mime-types) predate Node's built-in fetch. cli-kit requires Node >=22.12, which ships a spec-compliant fetch.

WHAT is this pull request doing?

  • http.ts now uses undici's fetch and FormData instead of node-fetch and form-data. undici is a single zero-dependency package (the same code that powers Node's built-in fetch).
  • Proxy support: global-agent patches Node's http module, which node-fetch used but fetch (undici) bypasses. To keep SHOPIFY_HTTP_PROXY / SHOPIFY_HTTPS_PROXY / SHOPIFY_NO_PROXY working for cli-kit's fetch traffic, requests go through an EnvHttpProxyAgent dispatcher when those variables are set. This is also why the PR depends on the npm undici package instead of the built-in fetch: Node 22 does not expose the proxy agent classes.
  • The useHttpsAgent internal option is gone: its https.Agent({keepAlive: true, rejectUnauthorized: true}) matches undici's defaults.
  • headers.raw() (a node-fetch extension) is replaced with standard Headers iteration and getSetCookie() in admin.ts and theme's storefront-session.ts.
  • http.test.ts moves from msw to undici's MockAgent: msw intercepts the patched global fetch and http.ClientRequest, neither of which undici's fetch uses.

Breaking API changes

  • @shopify/cli-kit/node/http no longer exports FetchError (a node-fetch class). Connection failures now surface as TypeError: fetch failed with the system error in cause. The one in-repo consumer (send-app-uninstalled-webhook.ts) now detects ECONNREFUSED through error.cause.
  • formData() returns the spec-compliant FormData (no getLengthSync/getBoundary/stream appends). No production code in the repo used the form-data-specific API.
  • Abort errors are DOMExceptions with slightly different messages (e.g. "The operation was aborted due to timeout"). The retry classifier already matches them.

How to test your changes?

Run any command that hits the network (shopify app dev, shopify theme dev with a password-protected store, shopify version update check). If you use a proxy, set SHOPIFY_HTTP_PROXY and confirm traffic goes through it.

Measuring impact

  • n/a: no new user-facing behavior intended; the fetch implementation is the same one bundled in Node.

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes

🤖 Generated with Claude Code

Use undici's fetch and FormData instead. undici replaces both packages
with a single zero-dependency package, and its EnvHttpProxyAgent keeps
support for the SHOPIFY_HTTP_PROXY family of environment variables,
which global-agent cannot provide for fetch traffic.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Aug 10, 2026
…ch-form-data

# Conflicts:
#	packages/cli-kit/package.json
#	pnpm-lock.yaml
@github-actions

Copy link
Copy Markdown
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/private/node/api.d.ts
@@ -1,4 +1,6 @@
-import { Headers } from 'form-data';
+interface Headers {
+    forEach(callbackfn: (value: string, key: string) => void): void;
+}
 export type API = 'admin' | 'storefront-renderer' | 'partners' | 'business-platform' | 'app-management';
 export declare const allAPIs: API[];
 export type NetworkRetryBehaviour = {
packages/cli-kit/dist/public/node/http.d.ts
@@ -1,7 +1,7 @@
 import { NetworkRetryBehaviour } from '../../private/node/api.js';
-import FormData from 'form-data';
-import { RequestInfo, RequestInit, Response } from 'node-fetch';
-export { FetchError, Request, Response } from 'node-fetch';
+import { FormData, Response } from 'undici';
+import type { RequestInfo, RequestInit } from 'undici';
+export { FormData, Request, Response } from 'undici';
 /**
  * Create a new FormData object.
  *
@@ -46,12 +46,8 @@ export declare function requestMode(preset?: RequestModeInput, env?: NodeJS.Proc
  */
 export declare function abortSignalFromRequestBehaviour(behaviour: RequestBehaviour): AbortSignal;
 /**
- * An interface that abstracts way node-fetch. When Node has built-in
- * support for "fetch" in the standard library, we can drop the node-fetch
- * dependency from here.
- * Note that we are exposing types from "node-fetch". The reason being is that
- * they are consistent with the Web API so if we drop node-fetch in the future
- * it won't require changes from the callers.
+ * An interface that abstracts away the fetch implementation (undici). The exposed
+ * types are consistent with the Web API.
  *
  * The CLI's fetch function supports special behaviours, like automatic retries. These are disabled by default through
  * this function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: @shopify/cli @shopify/cli package issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant