Test While Developing (TWD): An in-browser testing approach that validates full user workflows in real time. Combined with a secure BFF (Backend For Frontend) auth pattern, this project demonstrates production-grade authentication and testing practices.
backend-service/
├── routes/
│ ├── auth.ts # OAuth2 login/callback/logout
│ └── notes.ts # Protected API (requires session)
├── db/
│ ├── schema.ts # Users + Notes tables with relations
│ └── client.ts # Drizzle + LibSQL
├── session.ts # HttpOnly cookie config
└── tests/
├── auth.test.ts # Full OAuth flow validation
└── notes.test.ts # Protected API + user isolation
Key insight: The session cookie is HttpOnly and SameSite=Lax, so the frontend never sees tokens. The loader just calls /api/me to check if authenticated.
src/
├── pages/
│ ├── App/
│ │ ├── loader.ts # Fetches session + notes (redirects if 401)
│ │ ├── actions.ts # Form submission (create note via useFetcher)
│ │ └── App.tsx # Authenticated user home
│ └── Login/
│ └── loader.ts # Redirects auth users back to /
├── api/
│ ├── client.ts # Axios instance with credentials
│ ├── auth.ts # getSession() helper
│ └── notes.ts # fetchNotes(), createNote() helpers
├── components/
│ └── Notes.tsx # useFetcher form for notes
└── twd-test/
├── app.twd.test.ts # Full user journey (profile + notes)
├── login.twd.test.ts # Login redirect flow
└── authUtils.ts # Mock auth state
TWD is a radical new approach to testing web apps. Instead of running tests in Node.js with JSDOM or spinning up full E2E suites, TWD runs tests directly in the browser alongside your app.
- Real browser environment: No JSDOM quirks, no synthetic Node.js environment. Tests run where the app actually runs.
- Request mocking without complexity:
twd.mockRequest()instead of MSW handlers. - Testing Library built-in:
screenDom.getByRole()for semantic queries. - Instant feedback: Refresh = tests run. No test bundling, no startup overhead.
import { twd, userEvent, screenDom } from "twd-js";
import { describe, it, beforeEach } from "twd-js/runner";
import { mockAuthenticatedUser } from "./authUtils";
describe("Notes App", () => {
beforeEach(() => {
twd.clearRequestMockRules();
mockAuthenticatedUser({ id: "1", name: "John", email: "[email protected]" });
});
it("should create and display a new note", async () => {
// Mock the API response BEFORE the action
twd.mockRequest("createNote", {
method: "POST",
url: "/api/notes",
response: { id: 1, title: "Test", content: "My note", createdAt: Date.now() },
});
// Fill the form using Testing Library semantics
const titleInput = screenDom.getByLabelText("Title");
const submitBtn = screenDom.getByRole("button", { name: "Add note" });
await userEvent.type(titleInput, "Test");
await userEvent.click(submitBtn);
// Wait for the mocked request
await twd.waitForRequest("createNote");
// Assert the note appears in the DOM
const note = screenDom.getByText("Test");
note.should("be.visible");
});
});What's being tested: React Router action → useFetcher submission → API call → loader revalidation → DOM update. All real, all in the browser.
GET / (no session cookie)
↓
loaderApp checks /api/me → 401
↓
Redirect to /login
GET /auth/login
↓
Redirect to Auth0 (with client_id, redirect_uri, state)
GET /auth/callback?code=xxx&state=yyy
↓
BFF exchanges code for tokens (server-side only)
↓
BFF creates session, sets HttpOnly cookie
↓
Redirect to / (with session cookie)
GET / (with session cookie)
↓
loaderApp calls /api/me → 200 + user data
↓
loaderApp calls /api/notes → 200 + user's notes
↓
Page renders with profile + notes
POST / (useFetcher form)
↓
actionApp receives FormData, calls POST /api/notes
↓
BFF checks session, validates user_id, inserts note
↓
actionApp returns note, router revalidates loaderApp
↓
DOM updates with new note
cd backend-service
npm install
npm run db:push # Create SQLite tables
npm run dev # Starts on :3000Set up .env:
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_CLIENT_ID=xxxx
AUTH0_CLIENT_SECRET=xxxx
AUTH0_REDIRECT_URI=http://localhost:5173/auth/callbacknpm install
npm run dev # Starts on :5173 (proxies /auth and /api to :3000)Visit http://localhost:5173 and click "Log In" to start the OAuth flow.
In the browser (http://localhost:5173):
- Open DevTools console
- TWD tests run in the sidebar; watch them pass in real time.
cd backend-service
npm test # Runs Vitest with real SQLiteTest highlights:
auth.test.ts: Full OAuth flow (login redirect → callback → token exchange → session → logout).notes.test.ts: Protected API (verify user isolation, CRUD).
By exploring this project, you'll understand:
- OAuth2 Security: Why tokens stay server-side, how HttpOnly cookies work.
- React Router Data APIs: Loaders for data-before-render, actions for mutations, error boundaries.
- BFF Pattern: Why a backend layer protects your frontend from auth complexity.
- Testing Workflows: How TWD tests validate full user journeys faster than traditional E2E.
- Type Safety: Using TypeScript + Axios helpers to catch API bugs at compile time.
- backend-service/routes/auth.ts – Full OAuth implementation with session management.
- backend-service/routes/notes.ts – User-scoped API with session checks.
- src/pages/App/loader.ts – Auth guard + data loader pattern.
- src/pages/App/actions.ts – Form submission via React Router actions.
- src/twd-test/login.twd.test.ts – Login workflow validation.
- src/twd-test/app.twd.test.ts – Notes app workflow validation.
- Auth0 Docs
- React Router v7 Loaders/Actions
- Drizzle ORM
- TWD (Test While Developing) – The custom test runner powering this project