Search for a command to run...
In this guide, you'll learn how to install Medusa DTC Starter by Gorgo, configure both apps, and connect your first integration.
Install the starter one of two ways: scaffold a new project with Medusa's own CLI, or clone the repository and configure each app by hand.
yarn dlx create-medusa-app@latest --repo-url https://github.com/gorgojs/medusa-dtc-startertells to scaffold from the specified repository instead of the official Medusa starter, but the usual prompts for a project name and a database stay the same. By default, the command also runs migrations, creates an admin user, and seeds the database. Pass to skip all three.
Clone the repository and install dependencies:
1git clone https://github.com/gorgojs/medusa-dtc-starter.git2cd medusa-dtc-starter3yarn installCreate the backend's environment file and point at a real database:
Terminalcp apps/backend/.env.template apps/backend/.env
apps/backend/.envDATABASE_URL=postgres://postgres:@localhost:5432/medusa-dtc-starter
Run the migrations, create an admin user, seed the initial data, and start the backend:
1cd apps/backend2yarn medusa db:migrate3yarn medusa user -e admin@medusajs.com -p supersecret4yarn seed5yarn devOpen the Admin dashboard at , log in, and copy the publishable API key from Settings → Publishable API Keys.
Create the storefront's environment file with that key:
Terminalcp apps/storefront/.env.template apps/storefront/.env.local
apps/storefront/.env.localNEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_6c3...
Start the storefront:
1cd apps/storefront2yarn devThe storefront runs at . The repository root is set up for pnpm, so there starts both apps at once. With npm or yarn, start each app from its own directory.
Before the backend can run, set a real value for and :
apps/backend/.env1DATABASE_URL=postgres://postgres:@localhost:5432/medusa-dtc-starter2INTEGRATION_ENCRYPTION_KEY=supersecret
Everything else in ships with a working localhost default, apart from the SMTP block, which points at a placeholder host until you fill it in. What the rest of them do:
| Variable | What it does |
|---|---|
| Connection string for PostgreSQL. holds the database name it interpolates | |
| Encrypts the secret fields the Integration Module stores | |
| , | Signing secrets for tokens and cookies |
| Set it to on a live domain to send cookies over HTTPS only | |
| , , | Origins allowed to call the Store API, Admin, and authentication. Add your own domain here when you move either app off localhost |
| Redis instance for the cache, event bus, and workflow engine. Production uses it whenever it is set. points the cache alone at a separate instance | |
| Set it to to use in development too. Without it all three run in memory, which is enough for development | |
| , , , , | Mail server the transactional emails go through. Nothing is sent until you fill these in |
| , | Sender and reply-to addresses on those emails |
| , , , | Store details printed in the email templates |
| Language an email falls back to when the buyer's own is unknown | |
| Storefront address, used for the links inside emails and for revalidation webhooks | |
| Shared secret the backend sends with every revalidation webhook. It has to match the storefront's value | |
| , , , , | Move file uploads to S3-compatible storage. Read in production only |
| Storefront directory that Medusa's onboarding flow points at |
Replace every value before going to production.
For the storefront, you need at least the publishable API key from your Medusa backend:
apps/storefront/.env.localNEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_6c3...
The rest already ship with working localhost defaults:
| Variable | What it does |
|---|---|
| Publishable API key from Medusa Admin. Required | |
| Address the storefront calls the backend at | |
| The storefront's own address, used for absolute URLs in metadata, the sitemap, and | |
| Store name shown in the header, footer, and checkout, and used in the markup and the endpoint | |
| Country code the storefront falls back to when it cannot tell where a visitor is. The template's matches no seeded region, and then the first region the backend returns wins | |
| , | How a first-time visitor's country is detected. On Vercel or Cloudflare you can leave the provider unset, see the Set Up Country Detection documentation |
| Set it to and every response explains which country the middleware picked and why | |
| , | Address suggestions during checkout, see the Set Up Address Autocomplete documentation |
| Stripe publishable key, needed only if you take payments through Stripe | |
| , | Key and connected account for Medusa Cloud Payments. The key is used when is empty |
| Google Analytics 4 measurement ID. Leave it empty and the storefront loads no analytics script at all | |
| Has to match the same variable in , because the backend sends that value with every revalidation webhook. Change it before production | |
| , | Allow images from the Medusa Cloud bucket. Needed only when you deploy there |
| Node environment |
The Integration Module () is already registered in with an empty array, so there's nothing to wire up before you can add one. Once the backend is running, add a payment, fulfillment, or ERP integration and configure it from Medusa Admin, see the Browse and Add an Integration documentation.
The storefront's logs show:
Storefront logsError fetching regions. Did you set up regions in your Medusa Admin and define a NEXT_PUBLIC_MEDUSA_BACKEND_URL environment variable.
The cause is one of two things: either isn't set or points to the wrong place, or the backend has no region configured yet. Check that the backend is running and reachable at that URL. If there really are no regions, the seed script creates several, see the Customize Seed Data documentation.
The storefront's logs show one of two errors:
Storefront logsStripe key is missing. Set NEXT_PUBLIC_STRIPE_KEY environment variable.
The first one means the storefront tried to render Stripe Elements without a Stripe publishable key set.
Storefront logsStripe client secret is missing. Cannot initialize Stripe.
The second one means no Stripe payment session exists on the cart yet, usually because the selected region has no Stripe provider configured.
A first-time visitor lands in a region that has nothing to do with their location, usually the same one every time.
Set in , reload, and read the headers on the document request in the browser's network panel. names what answered: means the cookie was never cleared, so clear it or open a new private window; means nothing resolved and matched no seeded region either, so set it to a real country code; means never reached the server. See the Set Up Country Detection documentation for what each header means.
If the address field doesn't suggest anything as you type during checkout, the DaData key is probably missing. Create an account on DaData, set your key in , and leave set to . See the Set Up Address Autocomplete documentation for configuring autocomplete and connecting a different provider.
, , and in the backend's are allow-lists. If you move the storefront or Admin to a different port or domain, add that origin to the matching variable and restart the backend.