Medusa Starter Default
repository·master·Indexed 41 days ago
https://github.com/medusajs/medusa-starter-defaultA starter template for Medusa projects compatible with @medusajs/medusa version 2 and above. This repository provides guidance on creating custom API routes, extending the Medusa Admin with React-based widgets and pages, developing custom modules and services, and implementing module links. It also includes instructions for writing integration tests using medusa-test-utils and creating custom CLI scripts. Note: This starter is deprecated in favor of dtc-starter.
What's inside Medusa
- You can customize the Medusa Admin dashboard by adding new pages or injecting widgets into existing pages. These customizations are React-based and typically interact with custom API routes to provide merchants with additional functionality.
Create associations between modules using Module Links
masterModule Links allow you to form associations between data models belonging to different modules while preserving module isolation. Instead of direct foreign key dependencies between modules, you define a link using the
defineLinkutility.To create a link, you must reference the
.linkableproperty of the respective modules. For example, to associate aproductfrom theProductModulewith apostfrom a customBlogModule, you pass their linkable entities todefineLink.After defining your links in code, you must synchronize them with your database by running the migration command.
import BlogModule from "../modules/blog" import ProductModule from "@medusajs/medusa/product" import { defineLink } from "@medusajs/framework/utils" export default defineLink( ProductModule.linkable.product, BlogModule.linkable.post )How Medusa modules work
masterA module is a package of reusable functionalities that can be integrated into a Medusa application without affecting the overall system. Modules can be created as part of a plugin. A complete module typically consists of a data model (representing database tables), a service (containing business logic), and a module definition that exports the service to the Medusa container.Create a custom Medusa module
masterTo create a new module, follow these five steps:
- Create a Data Model: Define your database schema in a file under the
modelsdirectory usingmodel.definefrom@medusajs/framework/utils. - Create a Service: Define a class that extends
MedusaServiceand includes your models. This service holds your business logic. - Export Module Definition: Create an
index.tsfile in the module's root directory. Use theModuleutility to export the module name and its service. - Register the Module: Add the module to the
modulesarray in yourmedusa-config.tsusing theresolvekey pointing to the module path. - Run Migrations: Generate and apply database changes using the Medusa CLI.
- Create a Data Model: Define your database schema in a file under the
Use path parameters in API Routes
masterTo define dynamic path parameters, use directory names in the format
[param]. These parameters are then accessible viareq.params.- Single parameter: Create a directory like
[productId](e.g.,/api/products/[productId]/route.ts). - Multiple parameters: Nest multiple bracketed directories (e.g.,
/api/products/[productId]/variants/[variantId]/route.ts).
import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" export async function GET(req: MedusaRequest, res: MedusaResponse) { const { productId } = req.params; res.json({ message: `You're looking for product ${productId}` }) }- Single parameter: Create a directory like
How to create a custom CLI script
masterCustom CLI scripts allow you to execute custom logic through the Medusa CLI. To create one, create a TypeScript (
.ts) or JavaScript (.js) file inside thesrc/scriptsdirectory. The file must contain adefault exportof anasyncfunction.The function receives an
ExecArgsobject as its parameter, which includes acontainerproperty. Thecontaineris an instance of the Medusa Container, allowing you to resolve services and resources (e.g.,productmodule service) within your script.import { ExecArgs, } from "@medusajs/framework/types" export default async function myScript ({ container }: ExecArgs) { const productModuleService = container.resolve("product") const [, count] = await productModuleService.listAndCountProducts() console.log(`You have ${count} product(s)`) }Create a custom Admin Widget
masterA widget is a React component that can be injected into specific locations (zones) within existing Medusa Admin pages. To create a widget, you must export a default React component and a
configobject created viadefineWidgetConfig. Thezoneproperty in the configuration determines where the widget appears (e.g.,product.details.afterplaces it at the end of the product details page).import { defineWidgetConfig } from "@medusajs/admin-sdk" // The widget component const ProductWidget = () => { return ( <div> <h2>Product Widget</h2> </div> ) } // The widget's configuration export const config = defineWidgetConfig({ zone: "product.details.after", }) export default ProductWidgetConfigure Middleware for API Routes
masterTo apply middleware to specific routes, create an
/api/middlewares.tsfile. This file must export a configuration object using thedefineMiddlewaresfunction.matcher: A string or regular expression that defines which routes the middleware applies to.middlewares: An array of middleware functions. A middleware function typically receives(req, res, next)and must callnext()to proceed.
import { defineMiddlewares } from "@medusajs/framework/http" import type { MedusaRequest, MedusaResponse, MedusaNextFunction, } from "@medusajs/framework/http"; 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
masterTo execute a custom script, use the
medusa execcommand followed by the relative path to your script file.npx medusa exec ./src/scripts/my-script.tsCreate integration tests for API routes with medusa-test-utils
masterUse the
medusaIntegrationTestRunnerfrom themedusa-test-utilspackage to write integration tests for your Medusa API routes and workflows. The runner provides atestSuitefunction that gives you access to anapiobject for making HTTP requests and agetContainerfunction to access the Medusa dependency injection container. This allows you to test endpoints and verify their responses, status codes, and data structures.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!") }) }) }) } })Create a custom subscriber
masterSubscribers are asynchronous functions that handle events emitted within the Medusa application. To create one, place a TypeScript or JavaScript file in the
src/subscribersdirectory.Each subscriber file must export two specific members:
- An asynchronous default export function: This is the handler that executes when the event is triggered.
- A
configconstant: An object of typeSubscriberConfigthat defines which event the subscriber is listening to.
import { type SubscriberConfig, } from "@medusajs/framework" // subscriber function export default async function productCreateHandler() { console.log("A product was created") } // subscriber config export const config: SubscriberConfig = { event: "product.created", }Create Custom API Routes
masterAn API Route is a REST API endpoint defined using file-based routing. To create a route, place a
route.tsorroute.jsfile within the/src/apidirectory. The directory structure determines the URL path.For example, to create a
GETendpoint at/store/hello-world, create the file atsrc/api/store/hello-world/route.ts.import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"; export async function GET(req: MedusaRequest, res: MedusaResponse) { res.json({ message: "Hello world!", }); }