import { CodeTabs, CodeTab, Table } from "docs-ui"

export const metadata = {
  title: `Medusa JS SDK`,
}

# {metadata.title}

In this documentation, you'll learn how to install and use Medusa's JS SDK.

## What is Medusa JS SDK?

Medusa's JS SDK is a library to easily send requests to your Medusa application. You can use it in your admin customizations or custom storefronts.

---

## How to Install Medusa JS SDK?

The Medusa JS SDK is available in your Medusa application by default. So, you don't need to install it before using it in your admin customizations.

To install the Medusa JS SDK in other projects, such as a custom storefront, run the following command:

```bash npm2yarn
npm install @medusajs/js-sdk@latest @medusajs/types@latest
```

You install two libraries:

- `@medusajs/js-sdk`: the Medusa JS SDK.
- `@medusajs/types`: Medusa's types library, which is useful if you're using TypeScript in your development.

---

## Setup JS SDK

In your project, create the following `config.ts` file:

<Note>

For admin customizations, create this file at `src/admin/lib/config.ts`.

</Note>

<CodeTabs group="sdk-project">
  <CodeTab label="Admin" value="admin">

```ts title="src/admin/lib/config.ts"
import Medusa from "@medusajs/js-sdk"

export const sdk = new Medusa({
  baseUrl: import.meta.env.VITE_BACKEND_URL || "/",
  debug: import.meta.env.DEV,
  auth: {
    type: "session",
  },
})
```

  </CodeTab>
  <CodeTab label="Storefront" value="storefront">

```ts title="config.ts"
import Medusa from "@medusajs/js-sdk"

let MEDUSA_BACKEND_URL = "http://localhost:9000"

if (process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL) {
  MEDUSA_BACKEND_URL = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
}

export const sdk = new Medusa({
  baseUrl: MEDUSA_BACKEND_URL,
  debug: process.env.NODE_ENV === "development",
  publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
})
```

  </CodeTab>
</CodeTabs>

<Note title="Tip">

In the Medusa Admin you use `import.meta.env` to access environment variables becaues the Medusa Admin is built on top of [Vite](https://vitejs.dev/). Learn more in [this documentation](!docs!/learn/fundamentals/admin/environment-variables).

</Note>

### JS SDK Configurations

The `Medusa` initializer accepts as a parameter an object with the following properties:

<Table>
  <Table.Header>
    <Table.Row>
      <Table.HeaderCell>Property</Table.HeaderCell>
      <Table.HeaderCell className="w-2/5">Description</Table.HeaderCell>
      <Table.HeaderCell>Default</Table.HeaderCell>
    </Table.Row>
  </Table.Header>
  <Table.Body>
    <Table.Row>
      <Table.Cell>

      `baseUrl`

      </Table.Cell>
      <Table.Cell>

      A required string indicating the URL to the Medusa backend.

      </Table.Cell>
      <Table.Cell>

      \-

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `publishableKey`

      </Table.Cell>
      <Table.Cell>

      A string indicating the publishable API key to use in the storefront. You can retrieve it from the Medusa Admin.

      This is required for storefront applications. Otherwise, all requests will fail.

      </Table.Cell>
      <Table.Cell>

      \-

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `auth.type`

      </Table.Cell>
      <Table.Cell>

      A string that specifies the user authentication method to use.

      Possible types are:

      - `session`: The user is authenticated with a cookie session.
      - `jwt`: The user is authenticated with a JWT token that's passed in the Bearer authorization header.

      </Table.Cell>
      <Table.Cell>

      \-

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `auth.jwtTokenStorageKey`

      </Table.Cell>
      <Table.Cell>

      A string that, when `auth.type` is `jwt`, specifies the key of the JWT token in the storage specified in the `auth.jwtTokenStorageMethod` configuration.

      </Table.Cell>
      <Table.Cell>

      `medusa_auth_token`

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `auth.jwtTokenStorageMethod`

      </Table.Cell>
      <Table.Cell>

      A string that, when `auth.type` is `jwt`, specifies where the JWT token is stored. Possible values are:

      - `local` for the Local Storage.
      - `session` for the Session Storage.
      - `memory` to store it within the SDK for the current application's runtime.
      - `custom` to define custom storage using the `auth.storage` configuration. If `auth.storage` isn't defined, the SDK throws an error. This option is only available after Medusa v2.5.1.
      - `nostore` to not store the token.

      </Table.Cell>
      <Table.Cell>

      `local`

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `auth.storage`

      </Table.Cell>
      <Table.Cell>

      This option is only available after Medusa v2.5.1. It's an object or class that's used when `auth.jwtTokenStorageMethod` is `custom` to store the JWT token. It's useful when using the JS SDK in an environment where Local or Session Storage isn't available. The object or class must have the following methods:

      - `setItem`: A function that accepts a key and value to store the JWT token.
      - `getItem`: A function that accepts a key to retrieve the JWT token.
      - `removeItem`: A function that accepts a key to remove the JWT token from storage.

      Learn more in [this section](#use-custom-storage).

      </Table.Cell>
      <Table.Cell>

      \-

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `auth.fetchCredentials`

      </Table.Cell>
      <Table.Cell>

      By default, if `auth.type` is `session`, the `credentials: include` option is passed in [fetch requests](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#including_credentials) under the hood. However, some platforms or environments may not support passing this option.

      This option accepts a string to configure the value of `credentials` when the authentication type is `session`. It accepts one of the following values:

      - `include`: (default) to pass the `credentials: include` option.
      - `omit`: to pass the `credentials: omit` option.
      - `same-origin`: to pass the `credentials: same-origin` option.

      This option is only available after [Medusa v2.1.1](https://github.com/medusajs/medusa/releases/tag/v2.1.1).

      </Table.Cell>
      <Table.Cell>

      `local`

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `globalHeaders`

      </Table.Cell>
      <Table.Cell>

      An object of key-value pairs indicating headers to pass in all requests, where the key indicates the name of the header field.

      </Table.Cell>
      <Table.Cell>

      \-

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `apiKey`

      </Table.Cell>
      <Table.Cell>

      A string indicating the admin user's API key. If specified, it's used to send authenticated requests.

      </Table.Cell>
      <Table.Cell>

      \-

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `debug`

      </Table.Cell>
      <Table.Cell>

      A boolean indicating whether to show debug messages of requests sent in the console. This is useful during development.

      </Table.Cell>
      <Table.Cell>

      `false`

      </Table.Cell>
    </Table.Row>
    <Table.Row>
      <Table.Cell>

      `logger`

      </Table.Cell>
      <Table.Cell>

      Replace the logger used by the JS SDK to log messages. The logger must be a class or object having the following methods:

      - `error`: A function that accepts an error message to log.
      - `warn`: A function that accepts a warning message to log.
      - `info`: A function that accepts an info message to log.
      - `debug`: A function that accepts a debug message to log.

      </Table.Cell>
      <Table.Cell>

      JavaScript's [console](https://developer.mozilla.org/en-US/docs/Web/API/console) is used by default.

      </Table.Cell>
    </Table.Row>
  </Table.Body>
</Table>

---

## Send Requests to Custom Routes

The sidebar shows the different methods that you can use to send requests to Medusa's API routes.

To send requests to custom routes, the JS SDK has a `client.fetch` method that wraps the [JavaScript Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) that you can use. The method automatically appends configurations and headers, such as authentication headers, to your request.

For example, to send a request to a custom route at `http://localhost:9000/custom`:

<CodeTabs group="request-type">
  <CodeTab label="GET" value="get">

  ```ts
  sdk.client.fetch(`/custom`)
  .then((data) => {
    console.log(data)
  })
  ```
  
  </CodeTab>
  <CodeTab label="POST" value="post">

  ```ts
  sdk.client.fetch(`/custom`, {
    method: "post",
    body: {
      id: "123",
    },
  }).then((data) => {
    console.log(data)
  })
  ```

  </CodeTab>
  <CodeTab label="DELETE" value="delete">

  ```ts
  sdk.client.fetch(`/custom`, {
    method: "delete",
  }).then(() => {
    console.log("success")
  })
  ```

  </CodeTab>
</CodeTabs>

The `fetch` method accepts as a first parameter the route's path relative to the `baseUrl` configuration you passed when you initialized the SDK.

In the second parameter, you can pass an object of [request configurations](https://developer.mozilla.org/en-US/docs/Web/API/RequestInit). You don't need to configure the content-type to be JSON, or stringify the `body` or `query` value, as that's handled by the method.

The method returns a Promise that, when resolved, has the data returned by the request. If the request returns a JSON object, it'll be automatically parsed to a JavaScript object and returned.

---

## Medusa JS SDK Tips

### Use Tanstack (React) Query in Admin Customizations

In admin customizations, use [Tanstack Query](https://tanstack.com/query/latest) with the JS SDK to send requests to custom or existing API routes.

Tanstack Query is installed by default in your Medusa application.

<Note type="warning">

Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency.

</Note>

Use the [configured SDK](#setup-js-sdk) with the [useQuery](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery#usequery) Tanstack Query hook to send `GET` requests, and [useMutation](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation#usemutation) hook to send `POST` or `DELETE` requests.

For example:

<CodeTabs group="query-type">
  <CodeTab label="Query" value="query">

export const queryHighlights = [
  ["8", "useQuery", "Use Tanstack Query's `useQuery` to send a `GET` request."],
  ["9", "sdk.admin.product.list", "Use the SDK to send the request."],
  ["10", "queryKey", "Specify the key used to cache data."]
]

```tsx title="src/admin/widgets/product-widget.ts"
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Button, Container } from "@medusajs/ui"
import { useQuery } from "@tanstack/react-query"
import { sdk } from "../lib/config"
import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"

const ProductWidget = () => {
  const { data, isLoading } = useQuery({
    queryFn: () => sdk.admin.product.list(),
    queryKey: ["products"],
  })
  
  return (
    <Container className="divide-y p-0">
      {isLoading && <span>Loading...</span>}
      {data?.products && (
        <ul>
          {data.products.map((product) => (
            <li key={product.id}>{product.title}</li>
          ))}
        </ul>
      )}
    </Container>
  )
}

export const config = defineWidgetConfig({
  zone: "product.list.before",
})

export default ProductWidget
```

  </CodeTab>
  <CodeTab label="Mutation" value="mutation">

```tsx title="src/admin/widgets/product-widget.ts"
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { Button, Container } from "@medusajs/ui"
import { useMutation } from "@tanstack/react-query"
import { sdk } from "../lib/config"
import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"

const ProductWidget = ({ 
  data: productData,
}: DetailWidgetProps<HttpTypes.AdminProduct>) => {
  const { mutateAsync } = useMutation({
    mutationFn: (payload: HttpTypes.AdminUpdateProduct) => 
      sdk.admin.product.update(productData.id, payload),
    onSuccess: () => alert("updated product"),
  })

  const handleUpdate = () => {
    mutateAsync({
      title: "New Product Title",
    })
  }
  
  return (
    <Container className="divide-y p-0">
      <Button onClick={handleUpdate}>Update Title</Button>
    </Container>
  )
}

export const config = defineWidgetConfig({
  zone: "product.details.before",
})

export default ProductWidget
```

  </CodeTab>
</CodeTabs>

Refer to Tanstack Query's documentation to learn more about sending [Queries](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery#usequery) and [Mutations](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation#usemutation).

### Cache in Next.js Projects

Every method of the SDK that sends requests accepts as a last parameter an object of key-value headers to pass in the request.

In Next.js storefronts or projects, pass the `next.tags` header in the last parameter for data caching.

For example:

```ts highlights={[["2", "next"], ["3", "tags", "An array of tags to cache the data under."]]}
sdk.store.product.list({}, {
  next: {
    tags: ["products"],
  },
})
```

The `tags` property accepts an array of tags that the data is cached under.

Then, to purge the cache later, use Next.js's `revalidateTag` utility:

```ts
import { revalidateTag } from "next/cache"

// ...

revalidateTag("products")
```

Learn more in the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#fetch-optionsnexttags-and-revalidatetag).

### Use Custom Storage

<Note>

The `auth.storage` configuration is only available after Medusa v2.5.1.

</Note>

If you're using the JS SDK in an environment where Local Storage or Session Storage isn't available, such as in a React Native application, you can define custom logic to store the JWT token.

To do that, set the `auth.jwtTokenStorageMethod` configuration to `custom` and define the `auth.storage` configuration with the custom logic to store the JWT token.

For example, if you're using React Native's `AsyncStorage`:

```ts title="config.ts"
import AsyncStorage from "@react-native-async-storage/async-storage"
import Medusa from "@medusajs/js-sdk"

let MEDUSA_BACKEND_URL = "http://localhost:9000"

if (process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL) {
  MEDUSA_BACKEND_URL = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL
}

export const sdk = new Medusa({
  baseUrl: MEDUSA_BACKEND_URL,
  debug: process.env.NODE_ENV === "development",
  publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
  auth: {
    type: "jwt",
    jwtTokenStorageMethod: "custom",
    storge: AsyncStorage,
  },
})
```

In the `auth` configuration, you specify the `type` as `jwt`, the `jwtTokenStorageMethod` as `custom`, and the `storage` as `AsyncStorage`. So, the SDK uses `AsyncStorage` to store the JWT token.

#### Custom Storage Methods

The object or class passed to `auth.storage` configuration must have the following methods:

- `setItem`: A function that accepts a key and value to store the JWT token.
- `getItem`: A function that accepts a key to retrieve the JWT token.
- `removeItem`: A function that accepts a key to remove the JWT token from storage.
