Full-stack restaurant site: Next.js (App Router) + Neon Postgres (Drizzle ORM) + Better Auth + GSAP + Paystack + shadcn-style UI.
- Public site: animated homepage (GSAP parallax hero, scroll reveals), online menu/ordering with cart, reservation form.
- Auth: email/password sign-in & sign-up via Better Auth, with a
rolefield (customer/admin) on the user. - Ordering: cart →
/api/orderscreates the order server-side (prices are re-fetched from the DB, never trusted from the client) → redirects to a Paystack-hosted checkout →/api/paystack/webhookconfirms payment by verified signature, and/checkout/successdouble-checks via the verify API. - Admin dashboard (
/admin, gated byrole === "admin"): add/hide/delete menu items, view reservations, view orders.
- Create a project at https://neon.tech.
- Copy the pooled connection string into
.envasDATABASE_URL(copy.env.exampleto.envfirst).
- Get your test keys from https://dashboard.paystack.com/#/settings/developer.
- Put the secret key in
PAYSTACK_SECRET_KEY, the public key inNEXT_PUBLIC_PAYSTACK_PUBLIC_KEY. - In the Paystack dashboard, set your webhook URL to:
https://your-domain.com/api/paystack/webhook(for local testing, usengrokor Paystack's CLI tunnel).
Generate a secret:
openssl rand -base64 32Put it in BETTER_AUTH_SECRET.
npm install
npx drizzle-kit push # creates all tables in Neon from src/db/schema.ts
npx tsx scripts/seed.ts # adds a few starter menu itemsAfter signing up once through /sign-in, promote your user manually (Neon's
SQL editor, or any Postgres client):
update "user" set role = 'admin' where email = 'you@example.com';npm run devVisit http://localhost:3000.
src/
app/
page.tsx → homepage (Hero, philosophy, CTA)
menu/page.tsx → ordering page (cart + Paystack checkout)
reserve/page.tsx → reservation form
sign-in/page.tsx → Better Auth email/password
admin/ → admin dashboard (menu/reservations/orders)
checkout/success/page.tsx → Paystack verify + confirmation
api/
menu/ → public GET, admin POST/PATCH/DELETE
orders/ → create order + Paystack init, admin GET list
reservations/ → create + admin GET list
paystack/webhook/ → signature-verified payment confirmation
auth/[...all]/ → Better Auth handler
components/
hero.tsx, reveal.tsx, navbar.tsx → GSAP-driven UI
ui/ → button, card, input, label
db/
schema.ts → Drizzle tables (menu, orders, reservations, auth tables)
index.ts → Neon/Drizzle client
lib/
auth.ts, auth-client.ts, session.ts → Better Auth config + helpers
paystack.ts → Paystack init/verify
utils.ts → cn(), formatNaira()
- Money is stored in kobo (smallest unit) everywhere, matching how
Paystack expects amounts —
formatNaira()converts for display. - Swap the Unsplash hero/photo URLs for real photography before launch.
- Add a transactional email provider (Resend, Postmark) where the
// TODOcomments are, for order/reservation confirmations. - The GSAP
HeroandRevealcomponents are reusable — drop<Reveal>around any section to get the same scroll-in animation.