A modern course platform (LMS) built with Next.js 15, React 19, Stripe, Drizzle ORM, Shadcn UI, and PostgreSQL — focused on best practices, caching, and scalable project structure.
- Node.js
- Docker
- Stripe CLI (for local webhook testing)
git clone https://github.com/ACmaster7/course-platform
cd course-platform-projectCreate a .env file in the root of the project and copy values from .env.example.
❗️You must fill out all values in
.envbefore running the project — otherwise the app will throw an error on startup.
Example:
DB_PASSWORD=password
DB_USER=postgres
DB_NAME=course-platform
DB_HOST=localhost
DB_PORT=5432
...This project uses a PostgreSQL container managed by Docker Compose.
docker-compose up -dThis will:
- Start a
postgres:17.0container - Map container port
5432to your local machine’s5432(by default) - Use credentials and port from your
.envfile
If port 5432 is already in use on your machine change the DB_PORT value in .env (e.g., to 5434)
Since the project uses a canary version of Next.js, run with --force:
npm install --forceOnce the database container is up and dependencies are installed, generate the schema and migrate tables into the database:
npm run db:generate
npm run db:migrateThis will create and apply the database schema defined using Drizzle ORM.
You can now view and explore the database tables using:
npm run db:studio- Go to Clerk.com and create a free account.
- Create a new application.
- Get the following values from your Clerk dashboard and add them to your
.env:
**Note:
CLERK_WEBHOOK_SIGNING_SECRET– You will get this value in Step 7 when you create the Clerk webhook.
CLERK_SECRET_KEY=sk_test_...
CLERK_WEBHOOK_SIGNING_SECRET=whsec_...
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/
⚠️ Important: You must have your Clerk webhook set up before creating a user, so the PostgreSQL database can sync with Clerk. Otherwise, the app's logic will break.
👤 To test admin features: After creating a user, go to the Clerk dashboard → Users tab → Metadata section → Public → Edit → Set
roletoadmin→ Save. Make sure the webhook is set up so the change is reflected in your database.
- Go to Stripe Dashboard.
- Get your keys and create promo codes as needed.
- Add the following values:
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_PPP_50_COUPON_ID=your_coupon_id_here
STRIPE_PPP_40_COUPON_ID=your_coupon_id_here
STRIPE_PPP_30_COUPON_ID=your_coupon_id_here
STRIPE_PPP_20_COUPON_ID=your_coupon_id_here🧪 To test a purchase in dev mode, use the test card number:
4242 4242 4242 4242
You can enter any valid date, CVC, and name in the other inputs.
- Sign up at Arcjet.com and create a project.
- Copy your Arcjet key:
ARCJET_KEY=your_arcjet_key_hereNEXT_PUBLIC_SERVER_URL=http://localhost:3000
# Optional:
# TEST_IP_ADDRESS=1.187.0.0 # A test IP address from India to simulate regional coupons (e.g., PPP discounts)
To forward Clerk webhook events to your local server:
- In VS Code, open the Ports tab.
- Click “Forward a Port”, enter
3000, then hit Enter. - After the URL is generated (e.g.,
https://[random-chararcters]-3000.use2.devtunnels.ms/):- Right-click → Make port visibility Public
- Copy the URL and set up a webhook in Clerk:
[YOUR_PUBLIC_URL]/api/webhooks/clerk
To receive Stripe events locally:
stripe login
stripe listen --forward-to http://localhost:3000/api/webhooks/stripeCopy the whsec_... key it gives you and add it to .env:
STRIPE_WEBHOOK_SECRET=whsec_...Once everything is configured and installed:
npm run devTo populate the database with initial data:
http://localhost:3000/api/seedNavigate to that URL after starting the dev server.