Skip to content

Install

  • Bun 1.3+ or Node.js 20+
  • PostgreSQL 15+ running locally or remotely
  • A package manager: Bun (recommended), npm, pnpm, or yarn

PostgreSQL must be reachable before running db:push. Local setup: brew install postgresql@16 && brew services start postgresql@16.

At minimum you need the core engine and a database adapter:

Terminal window
bun add @porulle/core @porulle/adapter-postgres

For a full store with payments, file storage, and search:

Terminal window
bun add @porulle/core \
@porulle/adapter-postgres \
@porulle/adapter-stripe \
@porulle/adapter-local-storage \
@porulle/adapter-pg-search
PackagePurpose
@porulle/coreKernel: services, hooks, state machines, auth, runtime
@porulle/cliinit, dev, migrate, api-key, doctor commands
@porulle/sdkTyped TypeScript client + React Query bindings
@porulle/adapter-postgresPostgreSQL database adapter (required)
@porulle/adapter-stripeStripe payment adapter
@porulle/adapter-local-storageLocal filesystem media storage
@porulle/adapter-s3AWS S3 media storage
@porulle/adapter-r2Cloudflare R2 media storage
@porulle/adapter-meilisearchMeilisearch full-text search
@porulle/adapter-pg-searchPostgreSQL full-text search (no external service)
@porulle/adapter-resendResend transactional email
@porulle/adapter-sesAWS SES transactional email
@porulle/adapter-taxjarTaxJar tax calculation
@porulle/adapter-tax-manualFlat-rate / manual tax
@porulle/plugin-marketplaceMulti-vendor marketplace
@porulle/plugin-loyaltyPoints and tiers
@porulle/plugin-reviewsProduct reviews
@porulle/plugin-giftcardsGift card management
@porulle/plugin-posPoint-of-sale terminals, shifts, Z-reports
@porulle/plugin-appointmentsAppointment scheduling

Create a PostgreSQL database:

Terminal window
createdb porulle_dev
export DATABASE_URL="postgres://localhost:5432/porulle_dev"

Porulle uses Drizzle ORM for schema management. After creating your commerce.config.ts (see Quickstart), create a drizzle.config.ts:

drizzle.config.ts
import { defineConfig } from "drizzle-kit";
import { getSchemaFiles } from "@porulle/core";
export default defineConfig({
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL ?? "postgres://localhost:5432/porulle_dev",
},
schema: getSchemaFiles(),
});

getSchemaFiles() returns the core schema, which re-exports the Better Auth tables — so one entry covers both. Ask the package rather than hardcoding paths into node_modules: those break on the next layout change, and a path ending in src migrates a different copy than a published consumer’s server actually loads.

Plugins own their own tables. Add each plugin’s schema alongside:

import { getSchemaFiles } from "@porulle/core";
import { createRequire } from "node:module";
const resolve = createRequire(import.meta.url).resolve;
schema: [...getSchemaFiles(), resolve("@porulle/plugin-pos/schema")],

Push the schema:

Terminal window
bunx drizzle-kit push --config drizzle.config.ts

This creates all core tables (catalog, inventory, cart, orders, customers, pricing, promotions, fulfillment, media, webhooks, audit, jobs), Better Auth tables (user, session, account, organization, member), and any plugin tables you have installed.

Plugin tables are not discovered automatically. Each plugin you add needs its schema entry alongside getSchemaFiles(), as above, before db:push will create its tables.

Start the server and check the health endpoint:

Terminal window
bun run dev
curl http://localhost:4000/health
{ "status": "ok", "store": "My Store" }

The OpenAPI spec is at GET /api/doc. The interactive Scalar explorer is at GET /api/reference.