unsplash-js

Type-safe TypeScript client for the Unsplash API, generated directly from its OpenAPI spec.

SDK
npm
v8.0.1
2,193stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
65/100Good
Development Activity56
Maintenance48
Community56
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
69/100Good
Architecture80
Code Quality72
Innovation78
Learning Curve45

unsplash-js is Unsplash’s official JavaScript/TypeScript client, built as a thin wrapper around openapi-fetch and typed entirely from Unsplash’s own OpenAPI specification (public.json). Rather than hand-writing and maintaining a method per endpoint, the library generates its type definitions directly from the spec, so parameter shapes, query options, and response bodies stay accurate as Unsplash’s API evolves — regenerating types is a single pnpm gen away from support for any newly published endpoint.

The package exposes a single createApi() factory that returns a fully-typed .GET/.POST/.PUT/.DELETE/.PATCH client, plus a small trackNonHotLinkedPhotoView helper for satisfying Unsplash’s API guideline that requires pinging a view-tracking beacon when photos aren’t served through Unsplash’s own CDN. It ships as a dual ESM/CJS package with strict TypeScript declarations, a 5KB bundle-size budget enforced in CI, and no runtime dependencies beyond openapi-fetch itself.

What You Get

  • A single createApi() factory that returns a typed client covering every Unsplash REST endpoint (search, photos, users, collections, topics)
  • Compile-time parameter and response typing generated from Unsplash’s own OpenAPI spec, so autocomplete and type errors track the real API
  • A trackNonHotLinkedPhotoView helper for firing Unsplash’s required view-tracking beacon when photos are shown without hotlinking
  • Dual ESM/CJS builds with .d.mts/.d.cts declarations and a 5KB per-entry bundle-size budget enforced in CI

Common Use Cases

  • Building a photo search feature that queries /search/photos with query, orientation, and color filters
  • Server-side proxying of Unsplash requests to keep an access key confidential while browser clients call a baseUrl proxy endpoint
  • Fetching a user’s or collection’s photo feed and reading pagination totals from the X-Total response header
  • Recording accurate view counts for photos displayed outside Unsplash’s CDN via the tracking beacon

Under The Hood

Architecture The library is a single-file wrapper: src/index.ts (66 lines) calls openapi-fetch’s createFetchClient, parameterized with paths types generated from gen/unsplash.d.ts (itself produced by openapi-typescript from the checked-in public.json OpenAPI spec). It merges caller-supplied headers, an Accept-Version header, and an optional Authorization header via openapi-fetch’s mergeHeaders, deliberately avoiding a naive object spread that would silently drop Headers instances or header-array inputs. A separate src/beacon.ts exposes a curried trackNonHotLinkedPhotoView helper that pings Unsplash’s view-tracking pixel, guarding a 20-photo-ID limit. The library adds no endpoint-specific methods or custom abstractions on top of openapi-fetch’s generic .GET/.POST/etc. client — it is intentionally a thin adapter, so any change centers on regenerating types from the OpenAPI spec rather than modifying application logic.

Tech Stack The only runtime dependency is openapi-fetch. The toolchain is built around spec-driven codegen: openapi-typescript turns public.json into gen/unsplash.d.ts, and a custom jq script (scripts/gen-alias.jq) derives convenience type aliases into gen/aliases.d.ts. Builds run through tsdown, producing dual ESM/CJS output with .d.mts/.d.cts declarations; linting and formatting use the Rust-based oxlint/oxfmt rather than ESLint/Prettier; tests run on Vitest 4; and size-limit enforces a 5KB budget per bundle entry. A Nix flake pins a reproducible dev shell. The package requires Node >=20 and ships only the dist/ directory.

Code Quality A single Vitest suite (test/index.test.ts) intercepts the underlying fetch call via a thrown sentinel to assert on request URLs and headers without hitting the network, covering default base URL, custom apiVersion, custom baseUrl, and accessKey authorization behavior. There is no bespoke error hierarchy — request failures surface through openapi-fetch’s typed { data, error } result shape, except the beacon helper, which throws an explicit range-check error above 20 photo IDs. TypeScript runs in strict mode with types generated from the OpenAPI spec providing compile-time endpoint validation, and CI runs format, lint, type generation, build, test, and bundle-size-regression checks on every pull request. Test coverage is narrow in scope (constructor/header behavior only) since per-endpoint logic is type-only rather than runtime code to exercise.

API Design The public surface is a single createApi() factory returning an openapi-fetch client typed against the generated paths, so .GET/.POST calls get parameter and response autocomplete for free — any newly published Unsplash endpoint becomes available immediately after regenerating types, with no per-endpoint maintenance in this library. mergeHeaders usage avoids a common footgun around combining Headers instances with plain objects, and the underlying fetch implementation is swappable globally or per-request for testing or polyfilling older runtimes. The tradeoff is discoverability: consumers work with OpenAPI-shaped parameter objects (params: { path, query }) rather than a hand-designed, flat-argument SDK, which is less approachable at a glance but scales cleanly as Unsplash’s API surface grows.

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