--- title: Storage adapter description: Connect queries, uploads, mutations, URL resolution, and pagination to product infrastructure. order: 20 toc: - id: implement-the-contract title: Implement the contract - id: separate-display-and-reference-urls title: Separate display and reference URLs - id: preserve-query-semantics title: Preserve query semantics --- ## Implement the contract {#implement-the-contract} `MediaAdapter` is the package's complete persistence boundary. Implement its asset queries, folder operations, uploads, updates, and deletes with the product's storage service. ```ts import type { MediaAdapter } from "@workspace/media" export const mediaAdapter: MediaAdapter = { listAssets: (query) => api.media.list(query), listFolders: () => api.media.listFolders(), upload: (input) => api.media.upload(input), createFolder: (input) => api.media.createFolder(input), updateFolder: (input) => api.media.updateFolder(input), deleteFolder: (id) => api.media.deleteFolder(id), moveAsset: (id, folderId) => api.media.move(id, folderId), renameAsset: (id, name) => api.media.rename(id, name), updateFavorite: (id, favorite) => api.media.favorite(id, favorite), deleteAsset: (id) => api.media.delete(id), } ``` ## Separate display and reference URLs {#separate-display-and-reference-urls} Use `resolveUrl` for short-lived signed URLs or image transformations used while rendering. Use `resolveReference` for the value returned by the picker and stored in another document. When `resolveReference` is absent, images use `defaultMediaReference`, which preserves intrinsic dimensions in the URL. ## Preserve query semantics {#preserve-query-semantics} Treat `MediaAssetQuery` as an immutable request. Apply `allowedKinds`, `filter`, `folder`, and `keyword` before pagination, then return `hasNextPage` based on the filtered result. This keeps desktop and mobile filters consistent.