Skip to main content

JavaScript SDK

Mercur uses Medusa’s JavaScript SDK to communicate with the backend APIs. The JS SDK provides a type-safe, promise-based interface for sending requests to Admin, Store, and Vendor APIs.
Mercur does not have a custom SDK. It uses Medusa’s official JS SDK, which works seamlessly with all Mercur endpoints, including custom marketplace routes.

Installation

In Admin and Vendor Panels

The Medusa JS SDK is available by default in your Medusa application. No installation is required for admin and vendor customizations.

In External Projects

For storefronts or external applications, install the SDK:
npm2yarn
npm install @medusajs/js-sdk@latest @medusajs/types@latest

Setup

Admin Panel

src/lib/client/client.ts
import Medusa from "@medusajs/js-sdk"

export const backendUrl = __BACKEND_URL__ ?? "/"

export const sdk = new Medusa({
  baseUrl: backendUrl,
})

Vendor Panel

src/lib/client/client.ts
import Medusa from "@medusajs/js-sdk"

export const backendUrl = __BACKEND_URL__ ?? "/"
export const publishableApiKey = __PUBLISHABLE_API_KEY__ ?? ""

export const sdk = new Medusa({
  baseUrl: backendUrl,
  publishableKey: publishableApiKey,
})

Storefront (Next.js)

src/lib/config.ts
import Medusa from "@medusajs/js-sdk"

const MEDUSA_BACKEND_URL = process.env.MEDUSA_BACKEND_URL || "http://localhost:9000"

export const sdk = new Medusa({
  baseUrl: MEDUSA_BACKEND_URL,
  debug: process.env.NODE_ENV === "development",
  publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
})
Learn more about SDK configurations in the Medusa JS SDK documentation.

Authentication

The JS SDK handles authentication automatically by storing and managing authorization headers or sessions.

Login

await sdk.auth.login("user", "emailpass", {
  email: "[email protected]",
  password: "secret",
})

// All subsequent requests are authenticated

Logout

await sdk.auth.logout()

// User is logged out, subsequent requests are unauthenticated

Authentication Guide

Learn more about authentication methods, storage options, and API keys in Medusa’s authentication guide.

Using SDK with Medusa Core APIs

The JS SDK provides methods for all Medusa core APIs. These work identically in Mercur.

Store API

In storefronts, use the SDK’s built-in store methods for core commerce operations:
// List products
const { products } = await sdk.store.product.list({
  limit: 20,
  country_code: "us",
})

// Manage cart
const { cart } = await sdk.store.cart.create({ region_id: "reg_123" })

await sdk.store.cart.createLineItem(cart.id, {
  variant_id: "variant_123",
  quantity: 1,
})

// Complete checkout
const { order } = await sdk.store.payment.completeCart(cart.id)

Store API Reference

View all available store methods in the Medusa documentation.

Using SDK with Mercur Endpoints

For Mercur’s marketplace-specific APIs, use the sdk.client.fetch method. This is the standard approach used across all Mercur applications.

Vendor Panel Examples

import { sdk } from "../../lib/client"

// List seller's products
const { products } = await sdk.client.fetch("/vendor/products", {
  method: "GET",
  query: {
    limit: 20,
    offset: 0,
  },
})

// Get single order with details
const { order } = await sdk.client.fetch(`/vendor/orders/${orderId}`, {
  method: "GET",
})

// Create product
const { product } = await sdk.client.fetch("/vendor/products", {
  method: "POST",
  body: {
    title: "New Product",
    // ... other fields
  },
})

// Update product
await sdk.client.fetch(`/vendor/products/${productId}`, {
  method: "POST",
  body: {
    title: "Updated Title",
  },
})

// Delete product
await sdk.client.fetch(`/vendor/products/${productId}`, {
  method: "DELETE",
})

Admin Panel Examples

import { sdk } from "../lib/client"

// List all sellers
const { sellers, count } = await sdk.client.fetch("/admin/sellers", {
  method: "GET",
  query: {
    limit: 20,
    offset: 0,
    status: "ACTIVE",
  },
})

// Get seller details
const { seller } = await sdk.client.fetch(`/admin/sellers/${sellerId}`, {
  method: "GET",
  query: {
    fields: "id,email,name,created_at,store_status",
  },
})

// List requests
const { requests } = await sdk.client.fetch("/admin/requests", {
  method: "GET",
  query: {
    status: "pending",
    type: "product",
  },
})

// Review request
await sdk.client.fetch(`/admin/requests/${requestId}`, {
  method: "POST",
  body: {
    status: "accepted",
    reviewer_note: "Approved",
  },
})

// Invite seller
await sdk.client.fetch("/admin/sellers/invite", {
  method: "POST",
  body: {
    email: "[email protected]",
  },
})

Storefront Examples

import { sdk } from "../lib/config"

// Get seller by handle
const { seller } = await sdk.client.fetch(`/store/seller/${handle}`, {
  method: "GET",
  query: {
    fields: "+created_at,+reviews.rating,+reviews.customer_note",
  },
  cache: "no-cache",
})

// List product reviews
const { reviews } = await sdk.client.fetch("/store/reviews", {
  method: "GET",
  query: {
    product_id: "prod_123",
    fields: "*seller,+customer.id",
  },
})

// Create review
await sdk.client.fetch("/store/reviews", {
  method: "POST",
  body: {
    order_id: "order_123",
    reference: "product",
    reference_id: "prod_123",
    rating: 5,
    customer_note: "Excellent product!",
  },
})

Using with React Query

This section applies only to Admin Panel and Vendor Panel (React applications). The Next.js storefront uses server actions and Next.js caching instead of React Query.
Mercur’s Admin and Vendor panels use Tanstack Query extensively with the SDK for optimal caching and state management.

Query Hook Pattern

import { useQuery } from "@tanstack/react-query"
import { sdk } from "../../lib/client"

export const useSellers = (query?: Record<string, any>) => {
  const { data, ...rest } = useQuery({
    queryKey: ["sellers", query],
    queryFn: () =>
      sdk.client.fetch("/admin/sellers", {
        method: "GET",
        query,
      }),
  })

  return {
    sellers: data?.sellers,
    count: data?.count,
    ...rest,
  }
}

// Usage in component
function SellerList() {
  const { sellers, isLoading } = useSellers({ limit: 20 })

  if (isLoading) return <div>Loading...</div>

  return (
    <ul>
      {sellers?.map((seller) => (
        <li key={seller.id}>{seller.company_name}</li>
      ))}
    </ul>
  )
}

Mutation Hook Pattern

import { useMutation, useQueryClient } from "@tanstack/react-query"
import { sdk } from "../../lib/client"

export const useUpdateSeller = () => {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: ({ id, data }: { id: string; data: any }) =>
      sdk.client.fetch(`/admin/sellers/${id}`, {
        method: "POST",
        body: data,
      }),
    onSuccess: (_, { id }) => {
      queryClient.invalidateQueries({ queryKey: ["sellers"] })
      queryClient.invalidateQueries({ queryKey: ["sellers", id] })
    },
  })
}

// Usage in component
function UpdateSellerButton({ sellerId }) {
  const { mutate, isPending } = useUpdateSeller()

  const handleUpdate = () => {
    mutate({
      id: sellerId,
      data: { store_status: "ACTIVE" },
    })
  }

  return (
    <button onClick={handleUpdate} disabled={isPending}>
      {isPending ? "Updating..." : "Activate Seller"}
    </button>
  )
}

Next.js Storefront Pattern

The Next.js storefront uses a different pattern with server actions and Next.js caching:
src/lib/data/cart.ts
'use server'

import { sdk } from '../config'
import { revalidateTag } from 'next/cache'

export async function addToCart({ 
  variantId, 
  quantity, 
  countryCode 
}: {
  variantId: string
  quantity: number
  countryCode: string
}) {
  const cart = await getOrSetCart(countryCode)
  
  const headers = {
    ...(await getAuthHeaders())
  }

  await sdk.store.cart.createLineItem(
    cart.id,
    { variant_id: variantId, quantity },
    {},
    headers
  )
  
  // Revalidate Next.js cache
  const cartCacheTag = await getCacheTag('carts')
  revalidateTag(cartCacheTag)
}
This approach leverages:
  • Server Actions ('use server') for mutations
  • Next.js Cache (revalidateTag) for cache invalidation
  • Built-in SDK methods for core Medusa operations
  • sdk.client.fetch for Mercur-specific endpoints

Error Handling

The SDK throws a FetchError object when a request fails:
import { FetchError } from "@medusajs/js-sdk"

try {
  const { seller } = await sdk.client.fetch("/vendor/sellers/me", {
    method: "GET",
  })
} catch (error) {
  const fetchError = error as FetchError

  if (fetchError.status === 401) {
    // Redirect to login
    router.push("/login")
  } else if (fetchError.status === 404) {
    // Handle not found
    console.error("Seller not found")
  } else {
    // Handle other errors
    console.error(fetchError.message)
  }
}

Additional Resources