Medusa Eats

repository·main·Indexed 42 days ago

https://github.com/medusajs/medusa-eats

A fullstack food delivery platform demo built with Medusa 2.0 and Next.js 14 to showcase complex, multi-sided commerce scenarios. This architectural reference includes a Medusa backend (compatible with @medusajs/medusa 1.8.0+) and a Next.js frontend, featuring implementations for custom API routes, scheduled jobs, loaders, and event subscribers.

Tokens
18.1K
Snippets
87
Records
101
Agent score
54%

What's inside Medusa Eats

  1. Overview of Medusa Eats

    main

    Medusa Eats is a fullstack food delivery platform demo inspired by Uber Eats. It is designed to showcase the capabilities of Medusa 2.0 and Next.js 14 in building complex, multi-sided commerce applications.

    Note: This repository is an architectural reference and a hackathon demo; it is not an actively maintained starter project.

  2. Medusa Eats Project Structure

    main

    The project is divided into two primary directories:

    • /backend: A Medusa 2.0 project containing all customizations, including user authentication, order management, and core business logic.
    • /frontend: A Next.js project responsible for the user interface, including restaurant storefronts and various dashboards.
  3. Three-Sided Platform Architecture in Medusa Eats

    main

    Medusa Eats implements a three-sided marketplace model consisting of:

    • Customers: Users who browse restaurant menus and place orders.
    • Restaurants: Entities that receive order notifications, track food preparation, and manage menus.
    • Drivers: Users who receive job notifications and claim delivery routes.
  4. Real-time Order Workflow and Custom Modules

    main

    The platform utilizes Medusa 2.0 features to manage complex lifecycles:

    Real-time Workflow

    • Medusa Workflows: Long-running workflows handle the complete order lifecycle from selection to delivery.
    • Notifications: Automatic notifications for restaurants upon new orders.
    • Driver Matching: Automated system for finding available drivers.
    • Live Tracking: Real-time status updates for customers via Server Sent Events (SSE).

    Custom Modules

    To extend core commerce functionality, the project implements:

    • Restaurant module: Manages restaurant-specific entities and business logic.
    • Driver module: Handles driver profiles and delivery assignments.
    • Delivery module: Manages order fulfillment and tracking capabilities.
  5. Understand default middleware behavior

    main

    Medusa applies several default middlewares:

    • JSON Parsing: Applied to all routes. If the request has a Content-Type: application/json header, the body is available at req.body. You can override this for specific routes (e.g., using urlencoded) by exporting a config object from your route.ts.
    • Store CORS: Applied to all /store routes. Configure via STORE_CORS in medusa-config.js.
    • Admin CORS: Applied to all /admin routes. Configure via ADMIN_CORS in medusa-config.js.
    • Admin Authentication: Applied to all /admin routes by default (can be disabled via export const AUTHENTICATE = false in the route file).
  6. Create a custom loader in Medusa

    main

    A loader is a function that executes automatically when the Medusa application starts. To implement a custom loader, create a TypeScript or JavaScript file within the src/loaders directory of your project. The file must provide a default export of a function.

    export default function () {
      console.log(
        "[HELLO LOADER] Just started the Medusa application!"
      )
    }
  7. Use path parameters in custom routes

    main

    You can define dynamic parameters in your route paths by wrapping the parameter name in square brackets. These parameters are then accessible via req.params.

    Example: A file at /api/products/[productId]/route.ts allows you to capture a productId from the URL.

    Note: If using multiple parameters (e.g., /api/products/[productId]/variants/[variantId]/route.ts), each parameter must have a unique name. Duplicate parameter names will result in an error.

    import type {
      MedusaRequest,
      MedusaResponse,
      ProductService,
    } from "@medusajs/medusa"
    
    export async function GET(req: MedusaRequest, res: MedusaResponse) {
      const { productId } = req.params
    
      const productService: ProductService = req.scope.resolve("productService")
    
      const product = await productService.retrieve(productId)
    
      res.json({
        product,
      })
    }
  8. Create custom scheduled jobs

    main

    You can define custom scheduled jobs (cron jobs) by creating files within the /jobs directory of your project. A scheduled job consists of two main parts: a handler function and a config object.

    1. The Handler: An asynchronous function that contains the logic to be executed. It receives a single argument of type ScheduledJobArgs.
    2. The Config: An exported ScheduledJobConfig object that defines the job's identity and timing.

    To resolve services (like ProductService) within your job, use the container property provided in the handler arguments.

    import {
      ProductService,
      ScheduledJobArgs,
      ScheduledJobConfig,
    } from "@medusajs/medusa"
    
    export default async function myCustomJob({ container }: ScheduledJobArgs) {
      const productService: ProductService = container.resolve("productService")
    
      const products = await productService.listAndCount()
    
      // Do something with the products
    }
    
    export const config: ScheduledJobConfig = {
      name: "daily-product-report",
      schedule: "0 0 * * *", // Every day at midnight,
    }
  9. Configure middleware for custom routes

    main

    To apply middleware to specific routes, create an /api/middlewares.ts file. This file must export a config object of type MiddlewaresConfig.

    • matcher: A string or regular expression defining the route path.
    • middlewares: An array of middleware functions.
    • method (optional): A single HTTP method string or an array of method strings to restrict the middleware to specific verbs. If omitted, middleware applies to all methods for that matcher.
    import type {
      MiddlewaresConfig,
      MedusaRequest,
      MedusaResponse,
      MedusaNextFunction,
    } from "@medusajs/medusa"
    
    async function logger(
      req: MedusaRequest,
      res: MedusaResponse,
      next: MedusaNextFunction
    ) {
      console.log("Request received")
      next()
    }
    
    export const config: MiddlewaresConfig = {
      routes: [
        {
          matcher: "/store/custom",
          middlewares: [logger],
        },
      ],
    }
  10. Define custom API routes using file-based routing

    main

    Medusa uses a file-based routing system where files placed in the /api directory define the API endpoints. The file path determines the URL path. For example, a file at /api/store/custom/route.ts is accessible via the /store/custom endpoint.

    To define a route, create a route.ts file and export functions named after the supported HTTP methods.

    import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
    
    export async function GET(req: MedusaRequest, res: MedusaResponse) {
      res.json({
        message: "Hello world!",
      })
    }