---
title: createWithCache
description: |-
  Creates a utility function that executes an asynchronous operation 
   like `fetch` and caches the result according to the strategy provided.
  Use this to call any third-party APIs from loaders or actions.
   > Note:
   > Sometimes a request to a third-party API might fail, so you shouldn't cache the result. To prevent caching, throw when a request fails. If you don't throw, then the result is cached.
api_version: 2024-07
api_name: hydrogen
source_url:
  html: >-
    https://shopify.dev/docs/api/hydrogen/2024-07/utilities/caching/createwithcache
  md: >-
    https://shopify.dev/docs/api/hydrogen/2024-07/utilities/caching/createwithcache.md
---

# create​With​Cache

Creates a utility function that executes an asynchronous operation like `fetch` and caches the result according to the strategy provided. Use this to call any third-party APIs from loaders or actions.

**Note:** Sometimes a request to a third-party API might fail, so you shouldn\&#39;t cache the result. To prevent caching, throw when a request fails. If you don\&#39;t throw, then the result is cached.

## create​With​Cache(**[cacheOptions](#arguments-propertydetail-cacheoptions)**​)

### Parameters

* **cacheOptions**

  **CreateWithCacheOptions**

  **required**

### Returns

* **CreateWithCacheReturn\<T = unknown>**

### CreateWithCacheOptions

* cache

  An instance that implements the \[Cache API]\(https://developer.mozilla.org/en-US/docs/Web/API/Cache)

  ```ts
  Cache
  ```

* request

  The \`request\` object is used to access certain headers for debugging

  ```ts
  CrossRuntimeRequest
  ```

* waitUntil

  The \`waitUntil\` function is used to keep the current request/response lifecycle alive even after a response has been sent. It should be provided by your platform.

  ```ts
  WaitUntil
  ```

### CrossRuntimeRequest

* headers

  ```ts
  { [key: string]: any; get?: (key: string) => string; }
  ```

* method

  ```ts
  string
  ```

* url

  ```ts
  string
  ```

### CreateWithCacheReturn

This is a caching async function. Whatever data is returned from the \`actionFn\` will be cached according to the strategy provided. Use the \`CachingStrategy\` to define a custom caching mechanism for your data. Or use one of the built-in caching strategies: \`CacheNone\`, \`CacheShort\`, \`CacheLong\`.

* cacheKey

  ```ts
  CacheKey
  ```

* strategy

  ```ts
  AllCacheOptions
  ```

* actionFn

  ```ts
  ({ addDebugData }: CacheActionFunctionParam) => U | Promise<U>
  ```

interface Promise\<T> { /\*\* \* Attaches callbacks for the resolution and/or rejection of the Promise. \* @param onfulfilled The callback to execute when the Promise is resolved. \* @param onrejected The callback to execute when the Promise is rejected. \* @returns A Promise for the completion of which ever callback is executed. \*/ then\<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike\<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike\<TResult2>) | undefined | null): Promise\<TResult1 | TResult2>; /\*\* \* Attaches a callback for only the rejection of the Promise. \* @param onrejected The callback to execute when the Promise is rejected. \* @returns A Promise for the completion of the callback. \*/ catch\<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike\<TResult>) | undefined | null): Promise\<T | TResult>; }, interface Promise\<T> {}, Promise: PromiseConstructor, interface Promise\<T> { readonly \[Symbol.toStringTag]: string; }, interface Promise\<T> { /\*\* \* Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The \* resolved value cannot be modified from the callback. \* @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected). \* @returns A Promise for the completion of the callback. \*/ finally(onfinally?: (() => void) | undefined | null): Promise\<T>; }

```ts
interface Promise<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}, interface Promise<T> {}, Promise: PromiseConstructor, interface Promise<T> {
    readonly [Symbol.toStringTag]: string;
}, interface Promise<T> {
    /**
     * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
     * resolved value cannot be modified from the callback.
     * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
     * @returns A Promise for the completion of the callback.
     */
    finally(onfinally?: (() => void) | undefined | null): Promise<T>;
}
```

### CacheKey

The cache key is used to uniquely identify a value in the cache.

```ts
string | readonly unknown[]
```

### AllCacheOptions

Override options for a cache strategy.

* maxAge

  The maximum amount of time in seconds that a resource will be considered fresh. See \`max-age\` in the \[MDN docs]\(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#:\~:text=Response%20Directives-,max%2Dage,-The%20max%2Dage).

  ```ts
  number
  ```

* mode

  The caching mode, generally \`public\`, \`private\`, or \`no-store\`.

  ```ts
  string
  ```

* sMaxAge

  Similar to \`maxAge\` but specific to shared caches. See \`s-maxage\` in the \[MDN docs]\(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#s-maxage).

  ```ts
  number
  ```

* staleIfError

  Indicate that the cache should serve the stale response if an error occurs while revalidating the cache. See \`stale-if-error\` in the \[MDN docs]\(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#stale-if-error).

  ```ts
  number
  ```

* staleWhileRevalidate

  Indicate that the cache should serve the stale response in the background while revalidating the cache. See \`stale-while-revalidate\` in the \[MDN docs]\(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#stale-while-revalidate).

  ```ts
  number
  ```

### CacheActionFunctionParam

* addDebugData

  ```ts
  (info: AddDebugDataParam) => void
  ```

### AddDebugDataParam

* displayName

  ```ts
  string
  ```

* response

  ```ts
  Pick<Response, 'url' | 'status' | 'statusText' | 'headers'>
  ```

Examples

### Examples

* #### Example code

  ##### Description

  I am the default example

  ##### JavaScript

  ```js
  // In your app's `server.ts` file:
  import * as remixBuild from '@remix-run/dev/server-build';
  import {createWithCache, CacheLong} from '@shopify/hydrogen';
  // Use another `createRequestHandler` if deploying off oxygen
  import {createRequestHandler} from '@shopify/remix-oxygen';

  export default {
    async fetch(request, env, executionContext) {
      const cache = await caches.open('my-cms');
      const withCache = createWithCache({
        cache,
        waitUntil: executionContext.waitUntil.bind(executionContext),
        request,
      });

      // Create a custom utility to query a third-party API:
      const fetchMyCMS = (query) => {
        // Prefix the cache key and make it unique based on arguments.
        return withCache(['my-cms', query], CacheLong(), async (params) => {
          const response = await fetch('my-cms.com/api', {
            method: 'POST',
            body: query,
          });

          // Throw if the response is unsuccessful.
          // This is important to prevent the results from being cached.
          if (!response.ok) throw new Error(response.statusText);

          // Assuming the API returns errors in the body:
          const {data, error} = await response.json();

          // Validate data and throw to avoid caching errors.
          if (error || !data) throw new Error(error ?? 'Missing data');

          // Optionally, add extra information to show
          // in the Subrequest Profiler utility.
          params.addDebugData({displayName: 'My CMS query', response});

          return data;
        });
      };

      const handleRequest = createRequestHandler({
        build: remixBuild,
        mode: process.env.NODE_ENV,
        getLoadContext: () => ({
          // If you have one, update your env.d.ts to
          // include `fetchMyCMS` in `AppLoadContext`.
          fetchMyCMS,
        }),
      });

      return handleRequest(request);
    },
  };
  ```

  ##### TypeScript

  ```ts
  // In your app's `server.ts` file:
  import * as remixBuild from '@remix-run/dev/server-build';
  import {createWithCache, CacheLong} from '@shopify/hydrogen';
  // Use another `createRequestHandler` if deploying off oxygen
  import {createRequestHandler} from '@shopify/remix-oxygen';

  export default {
    async fetch(
      request: Request,
      env: Record<string, string>,
      executionContext: ExecutionContext,
    ) {
      const cache = await caches.open('my-cms');
      const withCache = createWithCache({
        cache,
        waitUntil: executionContext.waitUntil.bind(executionContext),
        request,
      });

      // Create a custom utility to query a third-party API:
      const fetchMyCMS = (query: string) => {
        // Prefix the cache key and make it unique based on arguments.
        return withCache(['my-cms', query], CacheLong(), async (params) => {
          const response = await fetch('my-cms.com/api', {
            method: 'POST',
            body: query,
          });

          // Throw if the response is unsuccessful.
          // This is important to prevent the results from being cached.
          if (!response.ok) throw new Error(response.statusText);

          const {data, error} = (await response.json()) as {
            data: unknown;
            error?: string;
          };

          // Validate data and throw to avoid caching errors.
          if (error || !data) throw new Error(error ?? 'Missing data');

          // Optionally, add extra information to show
          // in the Subrequest Profiler utility.
          params.addDebugData({displayName: 'My CMS query', response});

          return data;
        });
      };

      const handleRequest = createRequestHandler({
        build: remixBuild,
        mode: process.env.NODE_ENV,
        getLoadContext: () => ({
          // Make sure to update env.d.ts to
          // include `fetchMyCMS` in `AppLoadContext`.
          fetchMyCMS,
        }),
      });

      return handleRequest(request);
    },
  };
  ```
