Admin Dashboard with Auth
Part of the Full Stack Developer Roadmap
In short
Every company has an admin panel nobody shows off but everyone uses: users, orders, refunds, charts. Building one with proper roles proves you can be trusted with real data.
A protected, data-dense admin interface — charts, tables, and role-based views — demonstrating you can build the 'boring but essential' internal tools companies always need.

Think of it like this
The hostel warden's office
The warden's office has the register, the room keys and the attendance charts. The gate guard checks you're a resident, but only the warden and deputy get inside the office. Two checks, two levels of trust.
Watch it happen
Opening /admin/orders
1/5A fast first gate: no session cookie at all? Redirect to /login before any page code runs.
The big ideas
What you need to know
Roles, not just login
Admin, support and viewer can see different things. Store the role on the user and check it on the server.
Tables do the heavy lifting
Server-side pagination, sorting, filters and search. A table with 2 lakh rows must not load them all.
Audit every action
Log who refunded what and when. Admin power without a trail is a problem waiting to happen.
Charts from aggregates
Let the database do SUM and GROUP BY by day. Send the chart 30 numbers, not 30,000 orders.
See it in code
Walk through the code
1// proxy.ts (middleware.ts before Next.js 16): a quick first gate for /admin2import { NextResponse, type NextRequest } from "next/server";3export function proxy(req: NextRequest) {4 if (!req.cookies.get("session")) return NextResponse.redirect(new URL("/login", req.url));5}6export const config = { matcher: ["/admin/:path*"] };7 8// app/admin/page.tsx: the real check, right next to the data9import { redirect } from "next/navigation";10export default async function AdminPage() {11 const user = await getCurrentUser(); // verifies the session on the server12 if (user?.role !== "ADMIN") redirect("/403");13 const stats = await db.order.aggregate({ _sum: { totalPaise: true }, _count: true });14 return <StatsCards stats={stats} />;15}1/3Runs before /admin pages. It only checks a cookie exists, which is fast but not proof of anything.
Two files: a cheap cookie check at the edge, then the real session and role check inside the page that reads the data. Server actions need the same check again.
Save yourself the pain
Common mistakes
Oops
Relying only on middleware or proxy to protect admin pages and APIs.
Do this instead
Check the session and role again in each page, route handler and server action that reads or changes data.
Oops
Loading every row into the browser and paginating in JavaScript.
Do this instead
Paginate, sort and filter in the database, and send one page at a time.
Your mission
First milestone: a locked admin page
Quick check
Your admin page checks the role, but the /api/admin/refund route doesn't. What can a normal logged-in user do?
Go deeper
- 1Add real charts summarizing key metrics from your database.
- 2Implement a data table with sorting, filtering and pagination.
- 3Restrict access with proper role-based authorization, not just a hidden route.
More projects at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.