惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
A
About on SuperTechFans
MongoDB | Blog
MongoDB | Blog
Y
Y Combinator Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Security Latest
Security Latest
Project Zero
Project Zero
A
Arctic Wolf
L
LINUX DO - 热门话题
Microsoft Azure Blog
Microsoft Azure Blog
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cloudbric
Cloudbric
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
T
Threatpost
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
PCI Perspectives
PCI Perspectives
WordPress大学
WordPress大学
T
Tor Project blog
阮一峰的网络日志
阮一峰的网络日志
The Hacker News
The Hacker News
V
Visual Studio Blog
M
MIT News - Artificial intelligence
月光博客
月光博客
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
Attack and Defense Labs
Attack and Defense Labs
The Register - Security
The Register - Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
MyScale Blog
MyScale Blog
N
Netflix TechBlog - Medium
S
Security Affairs
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
AI
AI
S
Schneier on Security
L
LangChain Blog
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 叶小钗
量子位
H
Heimdal Security Blog
J
Java Code Geeks

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
React in Production — Ship It, Monitor It, Sleep at Night
Kushang Tailor · 2026-06-23 · via DEV Community

Read Time: ~15 minutes | The gap between "it works on my machine" and "it works for everyone, always"

Prerequisites: The full React Mastery Series — Parts 1–5


Part 6: The Finale 🎉


📌 What You'll Learn

By the end of this guide, you'll be able to:

  • ✅ Implement authentication with JWT and session cookies
  • ✅ Add OAuth (Google / GitHub login) in minutes
  • ✅ Set up CI/CD with GitHub Actions to automate your deployments
  • ✅ Catch and monitor errors in production with Sentry
  • ✅ Protect routes and handle auth state globally
  • ✅ Avoid the most common production mistakes React developers make
  • ✅ Understand what "production-ready" actually means in practice

🏁 You Made It Here — Now What?

Five articles in, you can build fast, well-structured, testable React apps. You know your hooks, your state patterns, your performance levers, and your Next.js data flows.

What you haven't done yet is ship something real, break it, watch it burn, and fix it gracefully — that's what production is.

This final part covers the things that separate a side project from a product people actually rely on: authentication that doesn't embarrass you, a CI/CD pipeline that ships your code automatically, and error monitoring that tells you something broke before your users do.


🔐 Authentication: JWT vs Session Cookies

Authentication is one of those topics where developers either overcomplicate it or cut too many corners. Let's get the fundamentals right first.

The Two Main Approaches

JWT (JSON Web Token)
└─ Token stored on client (localStorage or cookie)
└─ Server is stateless — doesn't store sessions
└─ Fast to verify (no DB lookup per request)
└─ Hard to invalidate before expiry (logout is tricky)

Session Cookies
└─ Session ID stored in an httpOnly cookie
└─ Session data lives on the server (DB or cache)
└─ Easy to invalidate (delete the session record)
└─ Requires DB lookup on every authenticated request

Recommendation for most apps: session cookies with httpOnly — more secure by default, easier logout, slightly more setup.

JWT makes sense when: you have multiple services that need to verify auth without sharing a database (microservices, third-party APIs).

Building JWT Auth in Next.js

Install dependencies:

npm install jsonwebtoken bcryptjs
npm install --save-dev @types/jsonwebtoken @types/bcryptjs

Step 1 — User login API route:

// app/api/auth/login/route.ts
import { NextRequest, NextResponse } from 'next/server';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';

const JWT_SECRET = process.env.JWT_SECRET!;

export async function POST(request: NextRequest) {
  const { email, password } = await request.json();

  // 1. Find user in database
  const user = await db.users.findUnique({ where: { email } });

  if (!user) {
    return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 });
  }

  // 2. Compare hashed password
  const isValid = await bcrypt.compare(password, user.passwordHash);

  if (!isValid) {
    return NextResponse.json({ error: 'Invalid credentials' }, { status: 401 });
  }

  // 3. Create token (expires in 7 days)
  const token = jwt.sign(
    { userId: user.id, email: user.email },
    JWT_SECRET,
    { expiresIn: '7d' }
  );

  // 4. Set token in httpOnly cookie (safer than localStorage)
  const response = NextResponse.json({ success: true });
  response.cookies.set('auth-token', token, {
    httpOnly: true,   // JS can't read this cookie
    secure: true,     // HTTPS only
    sameSite: 'lax',  // CSRF protection
    maxAge: 60 * 60 * 24 * 7, // 7 days in seconds
  });

  return response;
}

Step 2 — Verify token in middleware:

// middleware.ts (runs before every request)
import { NextRequest, NextResponse } from 'next/server';
import { jwtVerify } from 'jose'; // jose works in Edge runtime; jsonwebtoken doesn't

const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET!);

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('auth-token')?.value;

  // Protect /dashboard and all routes under it
  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    if (!token) {
      return NextResponse.redirect(new URL('/login', request.url));
    }

    try {
      await jwtVerify(token, JWT_SECRET);
      return NextResponse.next(); // Token valid — let the request through
    } catch {
      // Token invalid or expired
      const response = NextResponse.redirect(new URL('/login', request.url));
      response.cookies.delete('auth-token');
      return response;
    }
  }

  return NextResponse.next();
}

export const config = {
  matcher: ['/dashboard/:path*'], // Only run middleware on these routes
};

Step 3 — Logout:

// app/api/auth/logout/route.ts
import { NextResponse } from 'next/server';

export async function POST() {
  const response = NextResponse.json({ success: true });
  response.cookies.delete('auth-token');
  return response;
}


🌐 OAuth: Google & GitHub Login in Minutes

OAuth is the "Sign in with Google" button your users already expect. In Next.js, NextAuth.js (Auth.js v5) handles the entire OAuth dance for you.

npm install next-auth@beta

Configure Auth.js:

// auth.ts (project root)
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';
import GitHub from 'next-auth/providers/github';

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    GitHub({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    // Add custom data to the session
    session({ session, token }) {
      if (session.user && token.sub) {
        session.user.id = token.sub;
      }
      return session;
    },
  },
});

Mount the handler:

// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';
export const { GET, POST } = handlers;

Protect a page with the session:

// app/dashboard/page.tsx
import { auth } from '@/auth';
import { redirect } from 'next/navigation';

export default async function DashboardPage() {
  const session = await auth();

  if (!session) {
    redirect('/login'); // Not signed in — send them away
  }

  return (
    <div>
      <h1>Welcome, {session.user?.name}</h1>
      <p>Logged in as {session.user?.email}</p>
    </div>
  );
}

Sign in / sign out buttons:

'use client';
import { signIn, signOut } from 'next-auth/react';

export function AuthButtons() {
  return (
    <div>
      <button onClick={() => signIn('google')}>Sign in with Google</button>
      <button onClick={() => signIn('github')}>Sign in with GitHub</button>
      <button onClick={() => signOut()}>Sign Out</button>
    </div>
  );
}

Add GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, and NEXTAUTH_SECRET to your .env.local, and you have fully working social login.


🔄 CI/CD with GitHub Actions

Shipping manually is how bugs reach production. A CI/CD pipeline runs your tests, checks your build, and deploys automatically — every single time you push code.

What the Pipeline Does

Developer pushes code to GitHub
         ↓
GitHub Actions triggers automatically
         ↓
Install dependencies
         ↓
Run linter (ESLint)
         ↓
Run type checker (TypeScript)
         ↓
Run test suite (Jest)
         ↓
Build the app
         ↓
If all green → Deploy to Vercel ✅
If any red   → Block deploy, notify developer ❌

Full GitHub Actions Workflow

# .github/workflows/ci.yml

name: CI / CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  quality:
    name: Lint, Type Check & Test
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Run TypeScript check
        run: npx tsc --noEmit

      - name: Run tests
        run: npm test -- --coverage --watchAll=false
        env:
          CI: true

      - name: Build app
        run: npm run build
        env:
          NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}

  deploy:
    name: Deploy to Vercel
    needs: quality          # Only runs if the quality job passes
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' # Only deploy from main branch

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'

Add VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID to your GitHub repository's Secrets (Settings → Secrets and variables → Actions). Get the token from Vercel's dashboard.

Now every pull request runs the full quality check. Only clean, tested, built code reaches production.


🚨 Error Monitoring with Sentry

Tests catch bugs before users see them. Sentry catches the ones that slip through anyway — and tells you exactly where, why, and how often.

Setup

npm install @sentry/nextjs
npx @sentry/wizard@latest -i nextjs

The wizard creates sentry.client.config.ts, sentry.server.config.ts, and sentry.edge.config.ts automatically. You just need your DSN from the Sentry dashboard.

sentry.client.config.ts:

import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  tracesSampleRate: 0.1,     // Capture 10% of transactions for performance
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0, // Always capture replay when there's an error
  environment: process.env.NODE_ENV,
});

Capturing Errors Manually

import * as Sentry from '@sentry/nextjs';

async function processPayment(orderId: string) {
  try {
    const result = await paymentGateway.charge(orderId);
    return result;
  } catch (error) {
    // Send to Sentry with context
    Sentry.captureException(error, {
      tags: { component: 'checkout', action: 'processPayment' },
      extra: { orderId },
    });
    throw error; // Re-throw — don't silently swallow it
  }
}

Setting User Context

After login, tell Sentry who the user is — errors become instantly attributable:

import * as Sentry from '@sentry/nextjs';

function onLoginSuccess(user: User) {
  Sentry.setUser({
    id: user.id,
    email: user.email,
    username: user.name,
  });
}

function onLogout() {
  Sentry.setUser(null); // Clear on logout
}

Now when an error hits Sentry, you see exactly which user experienced it, what they were doing, and the full stack trace — no guessing.


🗂️ Production Environment Variables

Your app has three environments, and each needs its own config:

# .env.local (development — never commit this)
DATABASE_URL=postgresql://localhost:5432/myapp_dev
NEXT_PUBLIC_API_URL=http://localhost:3000
JWT_SECRET=dev-secret-not-for-production

# Vercel → Project Settings → Environment Variables → Preview
DATABASE_URL=postgresql://host/myapp_staging
NEXT_PUBLIC_API_URL=https://staging.myapp.com
JWT_SECRET=staging-secret-32-chars-minimum

# Vercel → Project Settings → Environment Variables → Production
DATABASE_URL=postgresql://host/myapp_prod
NEXT_PUBLIC_API_URL=https://myapp.com
JWT_SECRET=prod-secret-64-chars-cryptographically-random

Rules to live by:

// ✅ NEXT_PUBLIC_ prefix = visible in the browser (safe for public URLs)
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

// ✅ No prefix = server-only (safe for secrets, DB URLs, API keys)
const dbUrl = process.env.DATABASE_URL;

// ❌ Never put secrets in NEXT_PUBLIC_ variables
// ❌ Never commit .env.local to Git (add it to .gitignore)
// ❌ Never hardcode secrets — even "temporarily"


🧱 Production-Ready Checklist

Before you consider any app production-ready, run through this honestly:

Security

□ All auth routes protected by middleware
□ Passwords hashed with bcrypt (never stored plain)
□ httpOnly cookies used (not localStorage for tokens)
□ Environment variables never committed to Git
□ API routes validate and sanitize all input
□ Rate limiting on login and sensitive endpoints
□ HTTPS enforced (Vercel does this automatically)

Reliability

□ Error Boundaries wrap independent UI sections
□ Sentry (or equivalent) capturing all unhandled errors
□ API routes return meaningful error messages + HTTP codes
□ Loading and error states handled in every data component
□ CI pipeline blocks deploys when tests fail

Performance

□ Images use next/image (WebP, lazy loading, no CLS)
□ Fonts loaded via next/font (no layout shift)
□ Route-based code splitting in place
□ No console.log left in production code
□ Largest Contentful Paint (LCP) < 2.5s

Observability

□ Error monitoring active (Sentry)
□ Performance monitoring configured
□ Structured logging in API routes
□ Alerts set up for error spikes


💣 Production Mistakes Worth Learning From

These are real patterns that get real apps in trouble — most of them avoidable with about 20 minutes of setup.

Mistake 1: Storing tokens in localStorage

// ❌ XSS attack can steal this
localStorage.setItem('token', jwtToken);

// ✅ httpOnly cookie — JavaScript can't touch it
response.cookies.set('auth-token', token, { httpOnly: true, secure: true });

Mistake 2: Not validating API inputs

// ❌ Trusting anything the client sends
export async function POST(req: NextRequest) {
  const { email, role } = await req.json();
  await db.users.create({ data: { email, role } }); // 😱 user just gave themselves 'admin'
}

// ✅ Validate the shape and strip unexpected fields
export async function POST(req: NextRequest) {
  const body = await req.json();
  const email = String(body.email ?? '').trim();

  if (!email || !email.includes('@')) {
    return NextResponse.json({ error: 'Invalid email' }, { status: 400 });
  }

  await db.users.create({ data: { email, role: 'user' } }); // role is hardcoded
}

Mistake 3: Exposing server errors to clients

// ❌ Leaks your stack trace and internals
return NextResponse.json({ error: error.message }, { status: 500 });

// ✅ Log the real error server-side, send a generic message
console.error('[API Error]', error);
Sentry.captureException(error);
return NextResponse.json({ error: 'Something went wrong.' }, { status: 500 });

Mistake 4: No rate limiting on auth routes

// Install: npm install @upstash/ratelimit @upstash/redis
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(5, '1 m'), // 5 attempts per minute
});

export async function POST(request: NextRequest) {
  const ip = request.ip ?? '127.0.0.1';
  const { success } = await ratelimit.limit(ip);

  if (!success) {
    return NextResponse.json({ error: 'Too many attempts.' }, { status: 429 });
  }

  // ... rest of login logic
}

Mistake 5: Forgetting to handle token expiry on the client

// ✅ Intercept 401 responses globally and redirect to login
export async function fetchWithAuth(url: string, options?: RequestInit) {
  const response = await fetch(url, options);

  if (response.status === 401) {
    // Token expired — send user back to login
    window.location.href = '/login';
    return;
  }

  return response;
}


🎓 The Full Journey — What You've Built Over This Series

You started with "What is React?" and you're finishing with production deployments, auth systems, and error monitoring. That's not a small leap. Here's everything you've learned across all six parts:

Part 1 — Foundations
   React, components, hooks, setup, file structure

Part 2 — Advanced Hooks & State
   Custom hooks, useReducer, Context, Redux, Zustand, Jotai

Part 3 — Performance
   React.memo, useMemo, useCallback, code splitting, virtualization

Part 4 — Full-Stack with Next.js
   App Router, Server Components, API routes, deployment

Part 5 — Testing & Debugging
   Jest, React Testing Library, async tests, Error Boundaries

Part 6 — Production
   Auth, OAuth, CI/CD, Sentry, security, production patterns

You now have the full picture. Not just "React developer" — full-stack, production-capable, test-covered React engineering.


🔗 Quick Resources


💬 You've Reached the End — What's Next for You?

Six articles. A complete React engineering curriculum. What are you building with it?

If you've followed this series from Part 1, I'd genuinely love to hear what you took away most — a concept that finally clicked, a project you shipped, or even something I got wrong. Drop it in the comments.

And if you're hungry for what comes after production React, the natural next steps are: React Native for mobile, Turborepo for monorepos, and tRPC for end-to-end type safety without REST boilerplate.


Go build something real. 🚀