4.5 KiB
Local Development Guide
Prerequisites
- Node.js 20+
- npm
- PostgreSQL (running locally or via Docker)
- A Contabo S3 bucket (or any S3-compatible storage)
1. Clone and Install
git clone <repo-url> && cd spomeniQR
npm install
2. Set Up Environment Variables
cp .env.example .env
Edit .env with your local values:
# Clerk - get from https://dashboard.clerk.com
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Database - use localhost for local Postgres
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/monuments
# Contabo S3
S3_ENDPOINT=https://eu2.contabostorage.com
S3_REGION=eu-2
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
S3_BUCKET_NAME=monuments-images
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_APP_DOMAIN=localhost:3000
3. Set Up the Database
You need a PostgreSQL database. Options:
Option A: Docker (recommended)
Start only the database:
docker compose up db -d
This starts PostgreSQL on port 5432 with user postgres, password postgres, database monuments.
Option B: Local PostgreSQL
Create the database manually:
createdb monuments
Update DATABASE_URL in .env to match your local PostgreSQL credentials.
4. Run Prisma Migrations
npx prisma migrate dev --name init
This creates the tables and generates the Prisma client.
5. Set Up Contabo S3
- Log into your Contabo Object Storage dashboard
- Create a bucket named
monuments-images - Set bucket policy to public read (monument images must be publicly accessible)
- Create an API key (access key + secret key)
- Note your endpoint and region (e.g.
https://eu2.contabostorage.com, regioneu-2)
Set the CORS policy on the bucket to allow browser uploads:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": ["*"] },
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": ["arn:aws:s3:::monuments-images/*"]
}
]
}
Also set a CORS configuration:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT"],
"AllowedOrigins": ["http://localhost:3000"],
"ExposeHeaders": [],
"MaxAgeSeconds": 3600
}
]
6. Configure Clerk
- Go to Clerk Dashboard
- Create a new application
- Under Paths, set:
- Sign-in:
/sign-in - Sign-up:
/sign-up
- Sign-in:
- Under API Keys, copy the publishable key and secret key to your
.env - Under Domains, add
localhost:3000
7. Start Development Server
npm run dev
The app runs at http://localhost:3000.
8. Test Subdomain Routing Locally
Subdomain routing (eiffel-tower.testbed.mk) doesn't work on localhost. For local testing of subdomain pages:
-
Direct URL: Visit
http://localhost:3000/eiffel-tower(this simulates subdomain routing via the[subdomain]page route) -
Using /etc/hosts (optional, for realistic testing):
sudo nano /etc/hosts # Add: 127.0.0.1 eiffel-tower.testbed.mk 127.0.0.1 testbed.mkThen add this to your
.env:NEXT_PUBLIC_APP_DOMAIN=testbed.mkAnd modify
middleware.tstemporarily to also checklocalhostin development. -
Using the X-Subdomain header directly (for curl testing):
curl -H "X-Subdomain: eiffel-tower" http://localhost:3000/
9. Useful Commands
# Start dev server with hot reload
npm run dev
# Generate Prisma client (after schema changes)
npx prisma generate
# Create a new migration
npx prisma migrate dev --name your-migration-name
# Open Prisma Studio (DB GUI)
npx prisma studio
# Run linter
npm run lint
# Type check
npx tsc --noEmit
# Build for production
npm run build
10. Troubleshooting
"Prisma Client could not be generated"
npx prisma generate
Database connection refused
- Make sure PostgreSQL is running:
docker compose up db -d - Check
DATABASE_URLin.envmatches your DB credentials
Clerk authentication errors
- Verify your Clerk keys in
.envare correct - Ensure
localhost:3000is added as a domain in the Clerk dashboard
S3 upload fails
- Check that the bucket exists and permissions are set
- Verify the CORS configuration allows
http://localhost:3000 - Ensure the access key has both read and write permissions
Port 3000 already in use
# Kill whatever is on port 3000
lsof -ti:3000 | xargs kill -9
# Or use a different port
PORT=3001 npm run dev