Building a blog is often the "Hello World" of web development, but building a production-grade publication platform is a different beast. You need a fast, SEO-friendly public site for readers and a secure, powerful dashboard for your editorial team.
In this tutorial, we will build "TechChronicles," a dual-interface application:
- The Public Front: A high-performance Next.js app for readers.
- The Admin Console: A restricted area for editors to draft, review, and publish content.
We will focus on the architecture, the data flow, and solving the critical challenge of managing two distinct user types (Subscribers vs. Editors) without building two separate backends.
Phase 1: The Architecture
We want to avoid the "Monolithic WordPress" trap.
We also want to avoid over-engineering microservices.
Our Stack:
- Frontend: Next.js 14 (App Router)
- Backend: Node.js + Express (API)
- Database: PostgreSQL + Prisma ORM
- Auth: Rugi Auth (Centralized Identity)
The "Dual-Face" Strategy
Instead of mixing logic, we treat the "Public User" and the "Admin User" as different contexts.
-
reader.techchronicles.com-> Optimized for reading, caching, and speed. -
admin.techchronicles.com-> Optimized for editing, data management, and security.
Phase 2: Project Setup & Data Modeling
Let's start by defining our data. We need to store posts, but we also need to know who wrote them.
1. The Schema
In schema.prisma, we define a Post that links to an authorId.
// prisma/schema.prisma
model Post {
id String @id @default(uuid())
title String
slug String @unique
content String // Markdown or HTML
published Boolean @default(false)
authorId String // The Rugi Auth User ID
createdAt DateTime @default(now())
}
2. The API Server
Set up a simple Express server to serve these posts.
// src/server.ts
import express from 'express';
import { prisma } from './db';
const app = express();
// Public Endpoint: Everyone can see published posts
app.get('/api/posts', async (req, res) => {
const posts = await prisma.post.findMany({
where: { published: true }
});
res.json(posts);
});
// Admin Endpoint: Create a post (Wait, we need to protect this!)
app.post('/api/posts', async (req, res) => {
// TODO: Verify if the user is actually an editor
// const post = await prisma.post.create(...)
});
Phase 3: The Authentication Layer
Here is where most tutorials get bogged down. You start implementing "Sign up", "Login", "Forgot Password", "Email Verification", "JWT signing"... and suddenly your blog project is an auth project.
We will skip all that boilerplate by using Rugi Auth.
1. Initialize Rugi Auth
Run the initializer in a separate folder or container alongside your API.
npx rugi-auth init auth-service
2. Configure the "Dual" Apps
This is the secret sauce. We don't just create one "App". We create two distinct logical apps in Rugi Auth that share the same user pool.
- App 1: TechChronicles Public
- Goal: Allow readers to subscribe/comment.
- Type: Public
- Roles:
SUBSCRIBER
- App 2: TechChronicles Admin
- Goal: Allow staff to write news.
- Type: Confidential
- Roles:
EDITOR,ADMIN
3. Integrating the Protection
Now, back to our API. We can secure that POST route.
// src/middleware/auth.ts
import { verifyToken } from './utils'; // Your standard JWT verification
export const requireEditor = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
const user = await verifyToken(token);
// Check if they have the 'EDITOR' role specifically for the Admin App
const adminRole = user.roles.find(r =>
r.appId === process.env.ADMIN_APP_ID &&
r.name === 'EDITOR'
);
if (!adminRole) return res.status(403).send("Editors only.");
req.userId = user.id;
next();
};
Phase 4: Building the Frontends
The Public Reader Site (Next.js)
This is standard Next.js. We fetch the published posts.
// app/page.tsx
export default async function HomePage() {
const posts = await fetch('http://api/posts').then(r => r.json());
return (
<main>
<h1>Latest News</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</main>
);
}
The Admin Dashboard (React/Next.js)
This is where Rugi Auth shines. We need a login screen.
Instead of coding form state, error handling, and validation:
- Create a "Login" button.
- Redirect the user to your Rugi Auth hosted login page:
http://localhost:7100/auth/login?client_id=ADMIN_APP_ID
When they successfully log in as an Editor, they are redirected back to your dashboard with a secure token.
// app/admin/dashboard/page.tsx
"use client";
// This page is only accessible if you have a valid token
export default function Dashboard() {
const { createPost } = useApi();
return (
<div>
<h1>Editor Dashboard</h1>
<button onClick={() => createPost({ title: "New Article" })}>
Publish Article
</button>
</div>
);
}
Why This Approach Wins
- Separation of Concerns: Your public blog code doesn't know about "admin rights". It just displays content. Your API handles the gatekeeping.
- Unified Identity: If a Subscriber is promoted to an Editor, you just add a role in Rugi Auth. You don't need to migrate their account.
- Speed: We built a secure, role-protected CMS backend in minutes by offloading the complex user management logic.
Next Steps
- Add a "Comments" section to the public site, requiring the
SUBSCRIBERrole. - Add an "Admin-Only" route to manage users (which proxies requests to Rugi Auth's API).
Top comments (0)