Medusa B2B Starter
repository·main·Indexed 19 days ago
https://github.com/medusajs/b2b-starter-medusaA customizable B2B ecommerce starter built with Medusa 2.0 and Next.js 15. It features advanced business logic including company management, spending limits, and quote/approval workflows. The starter provides guidance on creating custom API routes, admin widgets, scheduled jobs, custom modules, CLI scripts, and integration testing using medusa-test-utils.
What's inside medusa-b2b-starter
- You can customize the Medusa Admin dashboard by adding new pages or injecting widgets into existing pages. These customizations can interact with custom API routes to provide merchants with specialized functionalities.
Core Features of Medusa B2B Commerce Starter
mainThe Medusa B2B Commerce Starter provides specialized features for business-to-business commerce, including:
- Company Management: Customers can manage their company and invite employees.
- Spending Limits: Company admins can assign spending limits to employees.
- Bulk add-to-cart: Customers can add multiple product variants to their cart simultaneously.
- Quote Management: Communication, acceptance, or rejection of quotes between customers and merchants.
- Order Edit: Merchants can modify orders or quotes (add/remove items, update quantities, and manage prices).
- Company Approvals: Mandated approvals from company admins before employees can finalize a cart.
- Merchant Approvals: Approval processes for orders to ensure compliance with business rules before fulfillment.
- Promotions: Manual and automatic promotions.
- Free Shipping Nudge: UI component showing progress toward free shipping thresholds.
- Full Ecommerce Support: Product pages, collections, categories, cart, checkout, user accounts, and order details.
- Next.js 15 Support: Utilizes App Router, Caching, Server components/actions, Streaming, and Static Pre-Rendering.
How modules work in Medusa
mainA module is a package of reusable functionalities that can be integrated into a Medusa application without affecting the overall system. A module consists of a Service (a class containing business logic) and a Module Definition (which registers the service with the Medusa framework). Once registered inmedusa-config.js, the module's service is available in the Medusa dependency injection container and can be resolved in other parts of the application, such as API routes.Create associations between modules using Module Links
mainModule Links allow you to form associations between data models belonging to different modules while preserving module isolation. Instead of creating direct foreign key dependencies between modules (which breaks isolation), you define a link that the Medusa framework manages to connect the two entities.
To create a link, use the
defineLinkutility from@medusajs/framework/utils. You must pass the linkable properties of the models you wish to associate.import HelloModule from "../modules/hello"; import ProductModule from "@medusajs/product"; import { defineLink } from "@medusajs/framework/utils"; export default defineLink( ProductModule.linkable.product, HelloModule.linkable.myCustom );Quickstart: Setup the Medusa B2B Commerce Starter
mainFollow these steps to set up both the Medusa backend and the Next.js storefront. This project requires Node 20, Postgres 15, Medusa 2.4, and Next.js 15.
1. Setup Backend
Navigate to the
backenddirectory, configure environment variables, install dependencies, and initialize the database with seed data.2. Setup Storefront
Navigate to the
storefrontdirectory, configure environment variables, and install dependencies.3. Configure Publishable Key
To connect the storefront to the backend, you must provide a publishable API key:
- Log in to the Medusa Admin at
http://localhost:9000/appusing:- Email:
admin@test.com - Password:
supersecret
- Email:
- Navigate to Settings > Publishable API Keys.
- Copy the token key for "Webshop".
- Open
storefront/.envand add the token to theNEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEYvariable.
4. Run the Project
Start the backend with
yarn devin the backend folder and the storefront withyarn devin the storefront folder.- Medusa Admin:
http://localhost:9000/app - Medusa Storefront:
http://localhost:8000
# Clone the repository git clone https://github.com/medusajs/b2b-starter-medusa.git ## Setup Backend cd ./backend cp .env.template .env yarn install # Install dependencies, setup database & seed data yarn install && yarn medusa db:create && yarn medusa db:migrate && yarn run seed && yarn medusa user -e admin@test.com -p supersecret -i admin # Start Medusa project - backend & admin yarn dev ## Setup Storefront cd ../storefront cp .env.template .env yarn install # After setting NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY in storefront/.env yarn dev- Log in to the Medusa Admin at
Use path parameters in API Routes
mainTo accept path parameters, create a directory within the route's path using the
[param]syntax. You can nest multiple parameter directories to accept multiple parameters.- Single parameter:
/api/products/[productId]/route.ts - Multiple parameters:
/api/products/[productId]/variants/[variantId]/route.ts
Access parameters via
req.params.import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa"; export async function GET(req: MedusaRequest, res: MedusaResponse) { const { productId } = req.params; res.json({ message: `You're looking for product ${productId}`, }); }- Single parameter:
Configure Middleware for API Routes
mainTo apply middleware to specific routes, create a
/api/middlewares.tsfile. This file must export a configuration object usingdefineMiddlewares.Each route configuration in the
routesarray requires:matcher: A string or regular expression defining the route to match.middlewares: An array of middleware functions(req, res, next) => void.
import { defineMiddlewares } from "@medusajs/medusa"; import type { MedusaRequest, MedusaResponse, MedusaNextFunction, } from "@medusajs/medusa"; async function logger( req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction ) { console.log("Request received"); next(); } export default defineMiddlewares({ routes: [ { matcher: "/store/custom", middlewares: [logger], }, ], });How to run a custom CLI script
mainTo execute a custom script, use the
npx medusa execcommand followed by the path to your script file.npx medusa exec ./src/scripts/my-script.tsUpdate the B2B Starter Project
mainWhen updating this starter to a newer version, follow these steps:
Update Packages
Run
yarn installin both thebackendandstorefrontprojects to update dependencies to their latest versions.Run Migrations
To apply changes to data models, run the following command in the
backendproject:npx medusa db:migrateMigration for Approval Module
If you are updating from a version that did not include the Approval module, you must run this script in the
backendproject to add approval settings to all existing companies:npx medusa exec src/scripts/create-approval-settings.ts# In the backend project npx medusa db:migrate # If migrating from a version without the Approval module npx medusa exec src/scripts/create-approval-settings.tsPass arguments to custom CLI scripts
mainYou can pass command-line arguments to your script. These arguments are accessible within your exported function via the
argsproperty of theExecArgsobject.To pass arguments, append them to the
npx medusa execcommand after the file path.import { ExecArgs } from "@medusajs/framework/types"; export default async function myScript({ args }: ExecArgs) { console.log(`The arguments you passed: ${args}`); }npx medusa exec ./src/scripts/my-script.ts arg1 arg2Create integration tests for API routes using medusa-test-utils
mainThe
medusa-test-utilspackage provides themedusaIntegrationTestRunnerutility to facilitate integration testing for API routes and workflows.To use it, call
medusaIntegrationTestRunnerand provide atestSuitefunction. This function receives an object containing:api: An object used to make HTTP requests (e.g.,api.get,api.post) to your Medusa instance.getContainer: A function to access the Medusa dependency injection container.
Inside the
testSuite, you can use standard testing framework globals likedescribeanditto structure your tests and assertions.import { medusaIntegrationTestRunner } from "medusa-test-utils" medusaIntegrationTestRunner({ testSuite: ({ api, getContainer }) => { describe("Custom endpoints", () => { describe("GET /store/custom", () => { it("returns correct message", async () => { const response = await api.get( `/store/custom` ) expect(response.status).toEqual(200) expect(response.data).toHaveProperty("message") expect(response.data.message).toEqual("Hello, World!") }) }) }) } })How to create a custom CLI script
mainCustom CLI scripts allow you to execute custom Medusa tooling via the Medusa CLI. To create one, create a TypeScript or JavaScript file inside the
src/scriptsdirectory. The file must provide adefault exportof an asynchronous function.The function receives an
ExecArgsobject as its parameter, which contains acontainerproperty. Thiscontaineris an instance of the Medusa Container, allowing you to resolve services and modules (e.g., usingcontainer.resolve()) within your script.import { ExecArgs, IProductModuleService } from "@medusajs/framework/types"; import { ModuleRegistrationName } from "@medusajs/framework/utils"; export default async function myScript({ container }: ExecArgs) { const productModuleService: IProductModuleService = container.resolve( ModuleRegistrationName.PRODUCT ); const [, count] = await productModuleService.listAndCount(); console.log(`You have ${count} product(s)`); }