xiaohongshu
xiaohongshu
mern-patterns
MERN stack patterns including React with Vite, Express middleware, MongoDB schemas, API Gateway architecture, session management, error handling, and testing strategies. Activate for MERN development, microservices architecture, and full-stack JavaScript applications.
Full skill instructions
Comprehensive MERN stack development patterns for the keycloak-alpha multi-tenant platform with 8 microservices.
Activate this skill when:
keycloak-alpha/
├── apps/
│ ├── web-app/ # Main React + Vite SPA
│ │ ├── src/
│ │ │ ├── components/
│ │ │ ├── pages/
│ │ │ ├── hooks/
│ │ │ ├── contexts/
│ │ │ ├── config/
│ │ │ ├── utils/
│ │ │ └── main.jsx
│ │ ├── vite.config.js
│ │ └── package.json
│ └── admin-portal/ # Admin dashboard (React + Vite)
│
├── services/
│ ├── api-gateway/ # Express API Gateway
│ ├── user-service/ # User management
│ ├── org-service/ # Organization management
│ ├── tenant-service/ # Multi-tenant provisioning
│ ├── notification-service/ # Email/SMS notifications
│ ├── billing-service/ # Stripe integration
│ ├── analytics-service/ # Usage analytics
│ └── keycloak-service/ # Keycloak integration
│
├── routes/
│ ├── api/
│ │ ├── users.js
│ │ ├── organizations.js
│ │ └── tenants.js
│ └── index.js
│
├── shared/
│ ├── types/
│ ├── utils/
│ └── constants/
│
└── package.json
// apps/web-app/vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@hooks': path.resolve(__dirname, './src/hooks'),
'@contexts': path.resolve(__dirname, './src/contexts'),
'@utils': path.resolve(__dirname, './src/utils'),
'@config': path.resolve(__dirname, './src/config'),
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
}
}
},
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
'vendor': ['react', 'react-dom', 'react-router-dom'],
'keycloak': ['keycloak-js'],
'ui': ['@chakra-ui/react', '@emotion/react']
}
}
}
},
optimizeDeps: {
include: ['react', 'react-dom', 'keycloak-js']
}
});
// apps/web-app/src/components/features/UserProfile/index.jsx
import { useState, useEffect } from 'react';
import { useAuth } from '@hooks/useAuth';
import { useToast } from '@chakra-ui/react';
import { updateUserProfile } from '@/api/users';
export function UserProfile() {
const { user } = useAuth();
const toast = useToast();
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
fetchProfile();
}, []);
async function fetchProfile() {
setLoading(true);
try {
const data = await getUserProfile(user.sub);
setProfile(data);
} catch (error) {
toast({
title: 'Error loading profile',
description: error.message,
status: 'error',
duration: 5000,
});
} finally {
setLoading(false);
}
}
async function handleSubmit(formData) {
try {
await updateUserProfile(user.sub, formData);
toast({
title: 'Profile updated',
status: 'success',
duration: 3000,
});
} catch (error) {
toast({
title: 'Update failed',
description: error.message,
status: 'error',
duration: 5000,
});
}
}
if (loading) return <Spinner />;
if (!profile) return <Alert>Profile not found</Alert>;
return <ProfileForm profile={profile} onSubmit={handleSubmit} />;
}
// apps/web-app/src/hooks/useAuth.js
import { createContext, useContext, useState, useEffect } from 'react';
import Keycloak from 'keycloak-js';
import { keycloakConfig } from '@config/keycloak.config';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [keycloak, setKeycloak] = useState(null);
const [authenticated, setAuthenticated] = useState(false);
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const kc = new Keycloak(keycloakConfig);
kc.init({
onLoad: 'check-sso',
checkLoginIframe: true,
pkceMethod: 'S256'
}).then(authenticated => {
setKeycloak(kc);
setAuthenticated(authenticated);
if (authenticated) {
setUser({
sub: kc.tokenParsed.sub,
email: kc.tokenParsed.email,
name: kc.tokenParsed.name,
orgId: kc.tokenParsed.org_id,
roles: kc.tokenParsed.realm_access?.roles || []
});
}
setLoading(false);
});
// Token refresh
kc.onTokenExpired = () => {
kc.updateToken(30).catch(() => {
kc.logout();
});
};
}, []);
const login = () => keycloak.login();
const logout = () => keycloak.logout();
const getToken = () => keycloak.token;
return (
<AuthContext.Provider value={{
authenticated,
user,
loading,
login,
logout,
getToken
}}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
};
// apps/web-app/src/utils/apiClient.js
import axios from 'axios';
import { useAuth } from '@hooks/useAuth';
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:4000/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// Request interceptor to add auth token
apiClient.interceptors.request.use(
async (config) => {
const token = await getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor for error handling
apiClient.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
// Redirect to login
window.location.href = '/login';
}
const errorMessage = error.response?.data?.message || error.message;
return Promise.reject(new Error(errorMessage));
}
);
export default apiClient;
// Typed API methods
export const api = {
users: {
getProfile: (userId) => apiClient.get(`/users/${userId}`),
updateProfile: (userId, data) => apiClient.put(`/users/${userId}`, data),
listUsers: (orgId) => apiClient.get(`/users?org_id=${orgId}`)
},
organizations: {
get: (orgId) => apiClient.get(`/organizations/${orgId}`),
create: (data) => apiClient.post('/organizations', data),
update: (orgId, data) => apiClient.put(`/organizations/${orgId}`, data)
}
};
// services/user-service/src/index.js
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import morgan from 'morgan';
import { connectDB } from './config/database.js';
import { errorHandler } from './middleware/errorHandler.js';
import { authMiddleware } from './middleware/auth.js';
import userRoutes from './routes/users.js';
const app = express();
// Security middleware
app.use(helmet());
app.use(cors({
origin: process.env.CORS_ORIGIN?.split(',') || 'http://localhost:3000',
credentials: true
}));
// Logging
app.use(morgan('combined'));
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Connect to MongoDB
await connectDB();
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'user-service' });
});
// API routes (protected)
app.use('/api/users', authMiddleware, userRoutes);
// Error handling
app.use(errorHandler);
const PORT = process.env.PORT || 5001;
app.listen(PORT, () => {
console.log(`User service running on port ${PORT}`);
});
// services/user-service/src/routes/users.js
import express from 'express';
import { body, param, query } from 'express-validator';
import { validate } from '../middleware/validate.js';
import { requireRole } from '../middleware/rbac.js';
import * as userController from '../controllers/user.controller.js';
const router = express.Router();
// GET /api/users - List users (org admin only)
router.get('/',
requireRole(['org_admin']),
query('org_id').optional().isString(),
query('page').optional().isInt({ min: 1 }),
query('limit').optional().isInt({ min: 1, max: 100 }),
validate,
userController.listUsers
);
// GET /api/users/:id - Get user by ID
router.get('/:id',
param('id').isUUID(),
validate,
userController.getUser
);
// POST /api/users - Create user (org admin only)
router.post('/',
requireRole(['org_admin']),
body('email').isEmail().normalizeEmail(),
body('firstName').trim().isLength({ min: 1, max: 50 }),
body('lastName').trim().isLength({ min: 1, max: 50 }),
body('orgId').isString(),
validate,
userController.createUser
);
// PUT /api/users/:id - Update user
router.put('/:id',
param('id').isUUID(),
body('firstName').optional().trim().isLength({ min: 1, max: 50 }),
body('lastName').optional().trim().isLength({ min: 1, max: 50 }),
validate,
userController.updateUser
);
// DELETE /api/users/:id - Delete user (org admin only)
router.delete('/:id',
requireRole(['org_admin']),
param('id').isUUID(),
validate,
userController.deleteUser
);
export default router;
// services/user-service/src/controllers/user.controller.js
import { UserModel } from '../models/User.js';
import { KeycloakService } from '../services/keycloak.service.js';
import { AppError } from '../utils/AppError.js';
export async function listUsers(req, res, next) {
try {
const { org_id, page = 1, limit = 20 } = req.query;
// Ensure user can only list users from their org (unless super admin)
const orgIdFilter = req.user.roles.includes('super_admin')
? org_id
: req.user.org_id;
if (!orgIdFilter) {
throw new AppError('Organization ID required', 400);
}
const users = await UserModel.find({ org_id: orgIdFilter })
.select('-password')
.limit(limit)
.skip((page - 1) * limit)
.sort({ createdAt: -1 });
const total = await UserModel.countDocuments({ org_id: orgIdFilter });
res.json({
users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
} catch (error) {
next(error);
}
}
export async function getUser(req, res, next) {
try {
const { id } = req.params;
const user = await UserModel.findById(id).select('-password');
if (!user) {
throw new AppError('User not found', 404);
}
// Ensure user can only access users from their org
if (user.org_id !== req.user.org_id && !req.user.roles.includes('super_admin')) {
throw new AppError('Access denied', 403);
}
res.json(user);
} catch (error) {
next(error);
}
}
export async function createUser(req, res, next) {
try {
const { email, firstName, lastName, orgId } = req.body;
// Verify org_id matches user's org (unless super admin)
if (orgId !== req.user.org_id && !req.user.roles.includes('super_admin')) {
throw new AppError('Cannot create user for different organization', 403);
}
// Create user in Keycloak
const keycloakService = new KeycloakService();
const keycloakUserId = await keycloakService.createUser({
email,
firstName,
lastName,
orgId
});
// Create user in MongoDB
const user = new UserModel({
keycloakId: keycloakUserId,
email,
firstName,
lastName,
org_id: orgId,
createdBy: req.user.sub
});
await user.save();
res.status(201).json({
id: user._id,
keycloakId: keycloakUserId,
email: user.email
});
} catch (error) {
next(error);
}
}
// services/user-service/src/models/User.js
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
keycloakId: {
type: String,
required: true,
unique: true,
index: true
},
email: {
type: String,
required: true,
lowercase: true,
trim: true,
index: true
},
firstName: {
type: String,
required: true,
trim: true
},
lastName: {
type: String,
required: true,
trim: true
},
org_id: {
type: String,
required: true,
index: true
},
roles: [{
type: String,
enum: ['org_admin', 'org_user', 'super_admin']
}],
metadata: {
type: Map,
of: String
},
createdBy: String,
updatedBy: String
}, {
timestamps: true,
toJSON: {
transform: (doc, ret) => {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
return ret;
}
}
});
// Compound index for org queries
userSchema.index({ org_id: 1, email: 1 }, { unique: true });
// Virtual for full name
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
// Pre-save hook
userSchema.pre('save', function(next) {
if (this.isModified('email')) {
this.email = this.email.toLowerCase();
}
next();
});
export const UserModel = mongoose.model('User', userSchema);
// services/org-service/src/models/Organization.js
import mongoose from 'mongoose';
const organizationSchema = new mongoose.Schema({
org_id: {
type: String,
required: true,
unique: true,
index: true
},
name: {
type: String,
required: true,
trim: true
},
domain: {
type: String,
required: true,
unique: true,
lowercase: true
},
settings: {
theme: {
type: String,
default: 'lobbi-base'
},
features: {
type: Map,
of: Boolean,
default: new Map()
},
branding: {
logoUrl: String,
primaryColor: String,
secondaryColor: String
}
},
subscription: {
plan: {
type: String,
enum: ['free', 'starter', 'professional', 'enterprise'],
default: 'free'
},
status: {
type: String,
enum: ['active', 'inactive', 'suspended'],
default: 'active'
},
billingCycle: {
type: String,
enum: ['monthly', 'annual']
},
stripeCustomerId: String,
stripeSubscriptionId: String
},
adminUsers: [{
userId: String,
email: String,
addedAt: Date
}],
status: {
type: String,
enum: ['active', 'inactive', 'suspended'],
default: 'active'
}
}, {
timestamps: true
});
export const OrganizationModel = mongoose.model('Organization', organizationSchema);
// services/api-gateway/src/index.js
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { authMiddleware } from './middleware/auth.js';
import { rateLimiter } from './middleware/rateLimit.js';
import { cacheMiddleware } from './middleware/cache.js';
const app = express();
// Rate limiting
app.use(rateLimiter);
// Authentication
app.use(authMiddleware);
// Service routing
const services = {
users: process.env.USER_SERVICE_URL || 'http://localhost:5001',
orgs: process.env.ORG_SERVICE_URL || 'http://localhost:5002',
tenants: process.env.TENANT_SERVICE_URL || 'http://localhost:5003',
notifications: process.env.NOTIFICATION_SERVICE_URL || 'http://localhost:5004',
billing: process.env.BILLING_SERVICE_URL || 'http://localhost:5005',
analytics: process.env.ANALYTICS_SERVICE_URL || 'http://localhost:5006'
};
// Proxy to microservices
Object.entries(services).forEach(([name, target]) => {
app.use(`/api/${name}`, createProxyMiddleware({
target,
changeOrigin: true,
pathRewrite: { [`^/api/${name}`]: '' },
onProxyReq: (proxyReq, req) => {
// Forward user context
if (req.user) {
proxyReq.setHeader('X-User-Id', req.user.sub);
proxyReq.setHeader('X-Org-Id', req.user.org_id);
proxyReq.setHeader('X-User-Roles', JSON.stringify(req.user.roles));
}
}
}));
});
app.listen(4000, () => {
console.log('API Gateway running on port 4000');
});
// services/api-gateway/src/config/session.js
import session from 'express-session';
import MongoStore from 'connect-mongo';
export const sessionConfig = session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URL,
ttl: 24 * 60 * 60, // 1 day
touchAfter: 24 * 3600 // lazy session update
}),
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24, // 24 hours
sameSite: 'lax',
domain: process.env.COOKIE_DOMAIN
},
name: 'lobbi.sid'
});
// shared/utils/AppError.js
export class AppError extends Error {
constructor(message, statusCode = 500, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
Error.captureStackTrace(this, this.constructor);
}
}
export class ValidationError extends AppError {
constructor(message, errors = []) {
super(message, 400);
this.errors = errors;
}
}
export class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 404);
}
}
export class UnauthorizedError extends AppError {
constructor(message = 'Unauthorized') {
super(message, 401);
}
}
export class ForbiddenError extends AppError {
constructor(message = 'Forbidden') {
super(message, 403);
}
}
// services/user-service/src/middleware/errorHandler.js
import { AppError } from '../utils/AppError.js';
export function errorHandler(err, req, res, next) {
let { statusCode, message, isOperational } = err;
// Default to 500 server error
if (!statusCode) {
statusCode = 500;
isOperational = false;
}
// Log error
console.error('Error:', {
message,
statusCode,
isOperational,
stack: err.stack,
url: req.url,
method: req.method,
user: req.user?.sub
});
// Send error response
res.status(statusCode).json({
error: {
message: isOperational ? message : 'Internal server error',
statusCode,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
}
// Async error wrapper
export function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
// services/user-service/tests/controllers/user.controller.test.js
import { listUsers, createUser } from '../../src/controllers/user.controller.js';
import { UserModel } from '../../src/models/User.js';
import { KeycloakService } from '../../src/services/keycloak.service.js';
jest.mock('../../src/models/User.js');
jest.mock('../../src/services/keycloak.service.js');
describe('UserController', () => {
describe('listUsers', () => {
it('should return paginated users for org', async () => {
const mockUsers = [
{ _id: '1', email: '[email protected]', org_id: 'org_1' },
{ _id: '2', email: '[email protected]', org_id: 'org_1' }
];
UserModel.find.mockReturnValue({
select: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
sort: jest.fn().mockResolvedValue(mockUsers)
});
UserModel.countDocuments.mockResolvedValue(2);
const req = {
query: { org_id: 'org_1', page: 1, limit: 20 },
user: { org_id: 'org_1', roles: ['org_admin'] }
};
const res = {
json: jest.fn()
};
const next = jest.fn();
await listUsers(req, res, next);
expect(res.json).toHaveBeenCalledWith({
users: mockUsers,
pagination: expect.objectContaining({
page: 1,
total: 2
})
});
});
});
});
// services/user-service/tests/integration/users.test.js
import request from 'supertest';
import { app } from '../../src/index.js';
import { connectDB, closeDB, clearDB } from '../setup.js';
beforeAll(async () => await connectDB());
afterEach(async () => await clearDB());
afterAll(async () => await closeDB());
describe('User API Integration', () => {
it('POST /api/users - should create user', async () => {
const userData = {
email: '[email protected]',
firstName: 'Test',
lastName: 'User',
orgId: 'org_test'
};
const response = await request(app)
.post('/api/users')
.set('Authorization', `Bearer ${mockAdminToken}`)
.send(userData)
.expect(201);
expect(response.body).toMatchObject({
email: userData.email
});
});
});
| Path | Purpose |
|---|---|
apps/web-app/ | React + Vite main application |
services/api-gateway/ | API Gateway with routing |
services/user-service/ | User management microservice |
services/org-service/ | Organization management |
routes/api/ | Shared route definitions |
shared/utils/ | Shared utilities and helpers |
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience