apollo-upload-client

GraphQL multipart file uploads for Apollo Client

Library
npm
v20.0.0
1,542stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
78/100Good
Development Activity72
Maintenance80
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
64/100Good
Architecture65
Code Quality72
Innovation58
Learning Curve60

apollo-upload-client is a terminating Apollo Link — the object Apollo Client uses to actually send network requests — that automatically detects File, FileList, or Blob instances anywhere within a GraphQL operation’s variables and, when found, sends a GraphQL multipart request per the community graphql-multipart-request-spec instead of a plain JSON POST. When no file-like values are present, it falls back to a regular GraphQL POST or GET request, so a single link handles both upload and non-upload operations transparently.

The package replaces Apollo Client’s standard HttpLink and works alongside the extract-files library (which walks the variables tree to detect and separate file values) to build the multipart form. It requires a compatible server implementation, such as graphql-upload, to correctly parse the incoming multipart payload and resolve file values back into readable streams in resolvers.

What You Get

  • UploadHttpLink — a drop-in replacement for Apollo Client’s standard terminating HTTP link that adds multipart-upload support
  • Automatic file detection in variables via the extract-files library, with no manual FormData construction required in application code
  • isExtractableFile — a re-exported helper for identifying which values in a variables tree count as uploadable files
  • formDataAppendFile — an overridable default strategy for appending a detected file to the outgoing FormData
  • ECMAScript-module-only distribution (.mjs deep imports, no CJS build) following the author’s ‘optimal JavaScript module design’ convention

Common Use Cases

  • Uploading user-selected files (profile photos, attachments, documents) through a GraphQL mutation instead of a separate REST upload endpoint
  • Supporting drag-and-drop or multi-file upload UIs where variables contain a FileList mixed with other scalar/object data
  • Migrating a React/Apollo app from a REST file-upload flow to a unified GraphQL API without introducing a second HTTP client just for uploads
  • Building a GraphQL client that must interoperate with a server implementing the graphql-multipart-request-spec (e.g. one using graphql-upload)

Under The Hood

Architecture - The package’s real logic lives in UploadHttpLink.mjs (roughly 300+ lines): it constructs an Apollo ApolloLink whose request handler inspects each operation’s variables, uses extract-files’s extractFiles to pull out any File/FileList/Blob values into a separate map while replacing them with null placeholders in the JSON, and — if any files were found — builds a multipart/form-data body (operations JSON part, a map part linking placeholders to form field names, and one form field per file, appended via the overridable formDataAppendFile helper) instead of a plain JSON body. If no files are found it delegates to a standard JSON POST/GET request path so non-upload operations pay no overhead. Tech Stack - Pure ESM (.mjs files, no CommonJS build, no bundler config) with @apollo/client, graphql, and rxjs as peer dependencies rather than direct dependencies, keeping the package thin and version-flexible against the host app’s own Apollo Client install; extract-files is the one direct runtime dependency doing the actual variables-tree file extraction. Code Quality - Every exported .mjs file has a matching .test.mjs (e.g. UploadHttpLink.test.mjs at ~900+ lines, formDataAppendFile.test.mjs), run via Node’s built-in test runner per the test script conventions visible in package.json; // @ts-check comments at the top of each source file enable TypeScript type-checking against JSDoc annotations without a separate .ts build step. API Design - There is deliberately no default/index module — consumers must deep-import from specific .mjs files per the exports map, which the author documents as ‘optimal JavaScript module design’ for tree-shaking; this is a stronger opinion than most libraries take and requires reading the README’s Exports section rather than a single top-level import, trading a small discoverability cost for leaner bundles.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search