643 lines
18 KiB
Plaintext
643 lines
18 KiB
Plaintext
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 (Medusa project)" value="admin-medusa-project">
|
|
|
|
```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="Admin (Medusa Plugin)" value="admin-medusa-plugin">
|
|
|
|
```ts title="src/admin/lib/config.ts"
|
|
import Medusa from "@medusajs/js-sdk"
|
|
|
|
export const sdk = new Medusa({
|
|
baseUrl: __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 Medusa Admin customizations that are created in a Medusa project, you use `import.meta.env` to access environment variables, whereas in customizations built in a Medusa plugin, you use the global variable `__BACKEND_URL__` to access the backend URL. You can learn more in the [Admin Environment Variables](!docs!/learn/fundamentals/admin/environment-variables) chapter.
|
|
|
|
</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 the [Authentication](./auth/overview/page.mdx#custom-authentication-storage-in-js-sdk) guide.
|
|
|
|
</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>
|
|
|
|
`include`
|
|
|
|
</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>
|
|
|
|
---
|
|
|
|
## Manage Authentication in JS SDK
|
|
|
|
The JS SDK supports different types of authentication methods and allow you to flexibly configure them.
|
|
|
|
To learn more about configuring authentication in the JS SDK and sending authenticated requests, refer to the [Authentication](./auth/overview/page.mdx) guide.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
---
|
|
|
|
## Handle Errors
|
|
|
|
If an error occurs in a request, the JS SDK throws a `FetchError` object. This object has the following properties:
|
|
|
|
- `status`: The HTTP status code of the response.
|
|
- `statusText`: The error code. For example, `Unauthorized`.
|
|
- `message`: The error message. For example, `Invalid credentials`.
|
|
|
|
You can use these properties to handle errors in your application.
|
|
|
|
For example:
|
|
|
|
<CodeTabs group="request-type">
|
|
<CodeTab label="Promise" value="promise">
|
|
|
|
```ts
|
|
sdk.store.customer.listAddress()
|
|
.then(({ addresses, count, offset, limit }) => {
|
|
// no errors occurred
|
|
// do something with the data
|
|
console.log(addresses)
|
|
})
|
|
.catch((error) => {
|
|
const fetchError = error as FetchError
|
|
|
|
if (fetchError.statusText === "Unauthorized") {
|
|
// redirect to login page
|
|
} else {
|
|
// handle other errors
|
|
}
|
|
})
|
|
```
|
|
|
|
</CodeTab>
|
|
<CodeTab label="Async/Await" value="async-await">
|
|
|
|
```ts
|
|
try {
|
|
const {
|
|
addresses,
|
|
count,
|
|
offset,
|
|
limit,
|
|
} = await sdk.store.customer.listAddress()
|
|
// no errors occurred
|
|
// do something with the data
|
|
console.log(addresses)
|
|
} catch (error) {
|
|
const fetchError = error as FetchError
|
|
|
|
if (fetchError.statusText === "Unauthorized") {
|
|
// redirect to login page
|
|
} else {
|
|
// handle other errors
|
|
}
|
|
}
|
|
```
|
|
|
|
</CodeTab>
|
|
</CodeTabs>
|
|
|
|
In the example above, you handle errors in two ways:
|
|
|
|
- Since the JS SDK's methods return a Promise, you can use the `catch` method to handle errors.
|
|
- You can use the `try...catch` statement to handle errors when using `async/await`. This is useful when you're executing the methods as part of a larger function.
|
|
|
|
In the `catch` method or statement, you have access to the error object of type `FetchError`.
|
|
|
|
An example of handling the error is to check if the error's `statusText` is `Unauthorized`. If so, you can redirect the customer to the login page. Otherwise, you can handle other errors by showing an alert, for example.
|
|
|
|
---
|
|
|
|
## Pass Headers in Requests
|
|
|
|
There are two ways to pass custom headers in requests when using the JS SDK:
|
|
|
|
1. Using the `globalHeaders` configuration: This is useful when you want to pass the same headers in all requests. For example, if you want to pass a custom header for tracking purposes:
|
|
|
|
```ts
|
|
const sdk = new Medusa({
|
|
// ...
|
|
globalHeaders: {
|
|
"x-tracking-id": "123456789",
|
|
},
|
|
})
|
|
```
|
|
|
|
2. Using the headers parameter of a specific method. Every method has as a last parameter a headers parameter, which is an object of headers to pass in the request. This is useful when you want to pass a custom header in specific requests. For example, to disable HTTP compression for specific requests:
|
|
|
|
```ts
|
|
sdk.store.product.list({
|
|
limit,
|
|
offset,
|
|
}, {
|
|
"x-no-compression": "false",
|
|
})
|
|
```
|
|
|
|
In the example above, you pass the `x-no-compression` header in the request to disable HTTP compression. You pass it as the last parameter of the `sdk.store.product.list` method.
|
|
|
|
<Note title="Tip">
|
|
|
|
The JS SDK appends request-specific headers to authentication headers and headers configured in the `globalHeaders` configuration. So, in the example above, the `x-no-compression` header is passed in the request along with the authentication headers and any headers configured in the `globalHeaders` configuration.
|
|
|
|
</Note>
|
|
|
|
---
|
|
|
|
## 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).
|