Skip to content

Running pcreative Commerce locally

Three processes and a database. On a clean machine, a few minutes from nothing to a browsable store.

apps/backend commerce engine :9000
apps/admin our own panel :7001/admin
themes/growshop-premium storefront :3000

Terminal window
docker compose up -d # Postgres :5434 (and a Redis only the old path uses)

The ports are deliberately not the standard ones (5432/6379): that way you can have several stores up at once without them treading on each other.

Terminal window
npm install # from the ROOT: this is a workspaces monorepo
Terminal window
cp apps/backend/.env.example apps/backend/.env
# point DATABASE_URL at the docker-compose port
cd apps/backend
npm run preparar # creates the schema (empty DB) or migrates, and prepares the shop
npm run seed # 6 categories and 24 demo products
npm run pcc -- rescate alta admin@example.com # first panel access: prints the password once
npm run dev:backend # :9000

The seed does two things that are not cosmetic:

  • It removes pp_system_default from the region and switches on the real providers. That one is the engine’s test provider: it charges €0 and marks the order as paid. It is the quietest failure in a fresh install, because the shop appears to “work”.
  • It gives the two shipping methods different prices; out of the box both are €10.

Without SMTP_HOST the system starts anyway: emails are written to the log instead of being sent. Configuring email is a go-live step, not a requirement for bringing the backend up.

Terminal window
cp themes/growshop-premium/.env.example themes/growshop-premium/.env.local

NEXT_PUBLIC_COMMERCE_KEY is the sales channel’s publishable key. If you do not have it to hand:

Terminal window
docker exec pcc-db psql -U pcreative -d pcreative_commerce \
-tAc "select token from api_key where type='publishable' limit 1"
Terminal window
npm run dev:theme # :3000
Terminal window
npm run dev:admin # :7001/admin

Under Themes you will see every folder in themes/ that has a valid theme.json. Open one and the customiser form is generated from its settings.schema.json: the panel does not know what settings a theme has until it reads them, and that is exactly why it works the same for a Next theme, an Astro one or a PHP one.

Saving writes themes/<id>/theme.override.json. In development the storefront re-reads the theme on every request, so the change shows up on reload — including derived colour scales, which recalculate themselves.


You may be looking at a panel with sections that are not in this repository. That is on purpose: a store can add its own — integrations with its suppliers, bespoke reports — and those live in that installation, not in the product.

The panel discovers them by itself: the backend announces what it has at GET /admin/capabilities and the menu adds them. Without that endpoint the panel works exactly the same, with no errors.

Terminal window
npm test # contracts: 130 tests, no backend
npm run theme -- validate themes/growshop-premium
npm run theme -- info themes/growshop-premium
npm run theme -- css themes/growshop-premium # the variables already resolved

React is pinned at the root. react and react-dom are in the root package.json devDependencies on purpose. The engine drags in React 18 (for the dashboard we keep disabled) and npm hoisted it to the root, where the theme’s next also lives: the theme ended up with two Reacts and the build died with minified error #31 (“object with keys {$$typeof, type…}”), which tells you nothing about the cause. Pinning React 19 at the root leaves a single copy.

You do not need to — and should avoid — aliasing react in next.config: that bypasses the react-server export conditions and breaks the Server Components layer with Cannot read properties of null (reading 'useOptimistic').

Reading the cart is not a server action. carritoActual() lives in lib/cart-read.ts and not in lib/cart.ts. A module marked "use server" turns all of its exports into actions, and calling an action during render does not mark the page as dynamic: Next was trying to prerender /carrito at build time and failing. Mutations go in cart.ts; reads stay out of it.