Comprehensive Architecture and Integration of MedusaJS 2.0 Engine
The MedusaJS 2.0 (v2.15.5) e-commerce engine manages all operations related to the product catalog, regional pricing, shopping cart, checkout process, Module Links, and customer accounts.
Crucial Note on Publishable API Key Configuration
After generating a key in the Medusa Admin panel, you must attach the created key to the **Main Sales Channel**; otherwise, products will not be displayed on the frontend.
Medusa v2 Module Links Architecture
In Medusa v2, modules are completely isolated, and database communication between independent module models (like reviews, questions, wishlists, and Strapi) is established via defineLink in the store-admin/src/links/ folder:
product-strapi.tsMapping between Medusa product ID and Strapi CMS content IDs.
review-product.tsConnection between buyer reviews and the corresponding Medusa product.
wishlist-customer.tsConnection between wishlist and the customer's profile.
question-product.tsConnecting submitted Q&As to catalog products.
store-admin/src/links/review-product.ts
typescriptimport { defineLink } from "@medusajs/framework/modules-sdk"import ProductModule from "@medusajs/medusa/product"import ProductReviewModule from "../modules/product-review" export default defineLink( ProductModule.linkable.product, ProductReviewModule.linkable.productReview)Useful Server & Seed Scripts for Database and Catalog Management
In the store-admin/src/scripts/ folder, there is a collection of ready-made scripts for seeding and managing products:
Initial database seed script (regions, shipping, products)
bashnpm run seedInject 30 complete sample products with variants and prices
bashnpx medusa exec ./src/scripts/seed-30-products.tsManual and forced synchronization of product index in Meilisearch
bashnpx medusa exec ./src/scripts/sync-meilisearch.tsComplete cleanup of products to start from scratch
bashnpx medusa exec ./src/scripts/delete-all-products.tsFrontend Product Fetching Layer (products.ts)
src/lib/data/products.ts
typescriptimport { sdk } from "@lib/config"import { HttpTypes } from "@medusajs/types"import { getAuthHeaders, getCacheOptions } from "./cookies"import { getLocaleHeader } from "@lib/util/get-locale-header"import { getRegion, retrieveRegion } from "./regions"import { isNetworkFetchError, warnMedusaUnreachable } from "@lib/util/medusa-fetch" export const listProducts = async ({ pageParam = 1, queryParams, countryCode, regionId, disableAuth = false,}: { pageParam?: number queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductListParams countryCode?: string regionId?: string disableAuth?: boolean}): Promise<{ response: { products: HttpTypes.StoreProduct[]; count: number } nextPage: number | null queryParams?: HttpTypes.FindParams & HttpTypes.StoreProductListParams}> => { if (!countryCode && !regionId) { throw new Error("Country code or region ID is required") } const limit = queryParams?.limit || 12 const _pageParam = Math.max(pageParam, 1) const offset = _pageParam === 1 ? 0 : (_pageParam - 1) * limit let region: HttpTypes.StoreRegion | undefined | null if (countryCode) { region = await getRegion(countryCode, disableAuth) } else { region = await retrieveRegion(regionId!, disableAuth) } if (!region) { return { response: { products: [], count: 0 }, nextPage: null } } const headers = { ...(disableAuth ? {} : await getAuthHeaders()), ...(await getLocaleHeader(disableAuth)), } as Record<string, string> const next = { ...(disableAuth ? { tags: ["store-products"] } : await getCacheOptions("store-products")), revalidate: 3600, } try { return await sdk.client .fetch<{ products: HttpTypes.StoreProduct[]; count: number }>( `/store/products`, { method: "GET", query: { limit, offset, region_id: region?.id, fields: "*variants.calculated_price,+variants.inventory_quantity,*variants.images,*options,*options.values,*variants.options,+metadata,+tags", ...queryParams, }, headers, next, cache: "force-cache", } ) .then(({ products, count }) => { const nextPage = count > offset + limit ? pageParam + 1 : null return { response: { products, count }, nextPage, queryParams, } }) } catch (error) { if (isNetworkFetchError(error)) { warnMedusaUnreachable("listProducts") return { response: { products: [], count: 0 }, nextPage: null, queryParams, } } throw error }}Email and Notification Service (Nodemailer SMTP & SendPulse)
The system for sending notification emails, contact forms, and order confirmations is implemented using Nodemailer (SMTP) and SendPulse with responsive templates:
Email service configuration in .env
envSMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USER=your-email@gmail.comSMTP_PASS=your-app-passwordSMTP_FROM_ADDRESS=noreply@yourdomain.comCONTACT_FORM_RECIPIENT=info@yourdomain.comSENDPULSE_API_ID=your_sendpulse_idSENDPULSE_API_SECRET=your_sendpulse_secret