The Pet Loft — Ecommerce Monorepo

A full-stack ecommerce platform for pet supplies built with Next.js, Convex, Clerk, and Turborepo.

Project Structure

ecommerce/
├── apps/
│   ├── storefront/          # Customer-facing store (Next.js + HeroUI) — port 3000
│   └── admin/               # Admin dashboard (Next.js + ShadCN) — port 3001
├── convex/                  # Convex backend (schema, functions, tests)
├── packages/
│   ├── types/               # Shared TypeScript types (Product, Order, User...)
│   ├── utils/               # Shared helper functions (formatPrice, slugify...)
│   └── convex/              # Shared Convex client provider (@repo/convex)
├── docs/                    # Convex reference docs + project documentation
├── package.json             # Root workspace config
└── turbo.json               # Turborepo pipeline config

For detailed documentation, see docs/project-documentation/.

Getting Started

1. Install dependencies

npm install

2. Set up Convex

  1. Go to convex.dev and create a new project
  2. Run npx convex dev from the root — this will prompt you to log in and link your project

3. Set up Clerk

  1. Go to clerk.com and create a new application
  2. In the Clerk Dashboard, create a JWT Template named convex (do not rename it)
  3. Copy your Publishable Key, Secret Key, and the JWT Issuer URL
  4. Set CLERK_JWT_ISSUER_DOMAIN in the Convex Dashboard environment variables

4. Configure environment variables

Copy the example env files and fill in your credentials:

cp apps/storefront/.env.example apps/storefront/.env.local
cp apps/admin/.env.example apps/admin/.env.local

5. Run the development servers

# Terminal 1 — Convex backend
npx convex dev

# Terminal 2 — Both Next.js apps
npm run dev

Packages

@repo/types

All shared TypeScript types used across both apps and the Convex backend.

import type { Product, Order, User } from "@repo/types";

@repo/utils

Shared utility functions.

import { formatPrice, slugify, formatDate } from "@repo/utils";

formatPrice(1999)           // → "$19.99"
slugify("Hello World!")     // → "hello-world"

@repo/convex

Shared Convex + Clerk provider. Both apps wrap their layout with this.

import { ConvexClientProvider } from "@repo/convex";

Authentication

This project uses Clerk for authentication and Convex for the backend.

  • Clerk handles sign-in/sign-up UI and JWT tokens
  • ConvexProviderWithClerk passes the JWT to the Convex backend
  • Convex functions access the user identity via ctx.auth.getUserIdentity()
  • Admin-only functions check the role field via requireAdmin()
  • Clerk webhooks sync user changes to the Convex users table

Scripts

Script What it does
npm run dev Run both apps in parallel via Turbo
npm run dev:storefront Run only the storefront
npm run dev:admin Run only the admin
npm run build Build both apps
npm run type-check TypeScript check across all workspaces
npm run test:once Run all tests once
npm run test Run tests in watch mode

Deployment

App Recommended Platform
Storefront Vercel (root dir: apps/storefront)
Admin Vercel (root dir: apps/admin)
Backend Convex Cloud (npx convex deploy)
Description
No description provided
Readme 27 MiB
Languages
TypeScript 94%
HTML 4.8%
CSS 0.7%
JavaScript 0.5%