unsplash-js
Type-safe TypeScript client for the Unsplash API, generated directly from its OpenAPI spec.
Repository Health
Technical Analysis
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
trackNonHotLinkedPhotoViewhelper for firing Unsplash’s required view-tracking beacon when photos are shown without hotlinking - Dual ESM/CJS builds with
.d.mts/.d.ctsdeclarations and a 5KB per-entry bundle-size budget enforced in CI
Common Use Cases
- Building a photo search feature that queries
/search/photoswith query, orientation, and color filters - Server-side proxying of Unsplash requests to keep an access key confidential while browser clients call a
baseUrlproxy endpoint - Fetching a user’s or collection’s photo feed and reading pagination totals from the
X-Totalresponse 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.
Used by 3 apps in this directory
Dub
Marketing · Analytics
The open-source link attribution platform for short links, conversion tracking, and affiliate programs — powering 100M+ clicks monthly.
LearnHouse
Learning Management · CMS
Open-source LMS with AI tutoring, real-time collaboration boards, live code execution, and built-in course monetization — self-hosted in minutes.
Typebot
Developer Tools · AI Development · No Code Platforms
Build sophisticated chatbots visually, embed them anywhere without iframes, and own your data — fully self-hostable with a modular block system and 30+ integrations.