codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
EDU›Central admin platform — many domains behind one hub›Step 2

Step 2

Project setup

0 views

Project setup

The base for an admin hub is a lean Next.js 16 + TypeScript strict + Tailwind 4. Admin is not a public site, so SEO and i18n are stripped.

1. Create the project

pnpm create next-app admin-platform \
  --typescript --tailwind --app --turbopack \
  --src-dir --import-alias "@/*"
cd admin-platform

2. Tighten TypeScript

tsconfig.json:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "forceConsistentCasingInFileNames": true,
    "exactOptionalPropertyTypes": true
  }
}

noUncheckedIndexedAccess forces undefined into every index access and removes a lot of subtle bugs later.

3. Install shadcn/ui

pnpm dlx shadcn@latest init
pnpm dlx shadcn@latest add button input select table card dialog scroll-area

4. Directory layout

src/
├── app/
│   ├── admin/
│   │   ├── layout.tsx           # AuthGuard
│   │   ├── dashboard/
│   │   └── blog/posts/
│   ├── api/
│   └── login/
├── shared/
│   ├── lib/
│   │   ├── db.ts
│   │   ├── auth/
│   │   └── audit.ts
│   ├── components/
│   │   ├── AdminResourceTable.tsx
│   │   ├── AuthGuard.tsx
│   │   └── Sidebar.tsx
│   └── constants/routes.ts

5. Environment variables

BLOG_DB_HOST=localhost
BLOG_DB_PORT=5435
BLOG_DB_NAME=blog
BLOG_DB_USER=postgres
BLOG_DB_PASSWORD=postgres

MARKET_DB_HOST=localhost
MARKET_DB_PORT=5433
# ...

KAKAO_CLIENT_ID=...
KAKAO_CLIENT_SECRET=...
JWT_SECRET_KEY=change-me-32-bytes
SESSION_EXPIRES_IN=86400
ADMIN_ALLOWED_EMAILS=admin@example.com

Domain-prefixed names make .env readable later.

6. Dev server

pnpm dev

Visit http://localhost:3000. We will replace the default page with /login and /admin/dashboard in the next steps.

Closing

Admin apps have more visual freedom than public sites. Staying with shadcn defaults or stacking on top of Radix primitives both work. What matters is "every page comes from the same component drawer" — the payoff lands around the third or fourth page.

Next

  • 03-multiple-pg-pools

← Step 1

Why a central admin hub

Step 3 →

Connecting multiple PostgreSQL pools