Skip to Content
System Architecture

System Architecture

FluxKit is a Next.js App Router application (src/) backed by Convex functions (convex/) with Better Auth and Polar wired through Convex components.

Runtime composition

src/app/layout.tsx sets the root provider stack and injects an auth token into the Convex client provider.

import { ConvexClientProvider } from "@/components/providers/ConvexClientProvider"; import { getToken } from "@/lib/auth/server"; export default async function RootLayout({ children, }: { children: React.ReactNode; }) { let token: string | null = null; try { token = (await getToken()) ?? null; } catch { token = null; } return ( <ConvexClientProvider initialToken={token}>{children}</ConvexClientProvider> ); }

ConvexClientProvider creates a ConvexReactClient and binds Better Auth client plugins.

import { ConvexBetterAuthProvider } from "@convex-dev/better-auth/react"; import { ConvexReactClient } from "convex/react"; import { authClient } from "@/lib/auth/client"; const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!); export function ConvexClientProvider({ children, initialToken, }: { children: React.ReactNode; initialToken?: string | null; }) { return ( <ConvexBetterAuthProvider client={convex} authClient={authClient} initialToken={initialToken} > {children} </ConvexBetterAuthProvider> ); }

Request and auth flow

  1. Browser request enters Next.js.
  2. src/proxy.ts performs redirect and coarse auth/rate checks.
  3. Route/layout renders; server layout fetches token with getToken().
  4. Client components call Convex queries/mutations over ConvexReactClient.
  5. Convex handlers re-check identity with ctx.auth.getUserIdentity().
const protectedPrefixes = [ "/dashboard", "/settings", "/mail", "/tasks", "/chat", "/calendar", "/users", "/faqs", "/pricing", ]; const isProtectedRoute = protectedPrefixes.some((prefix) => pathname.startsWith(prefix), ); const sessionCookie = getSessionCookie(request); if (isProtectedRoute && !sessionCookie) { const signInUrl = new URL("/sign-in", request.url); signInUrl.searchParams.set("redirect", pathname); return NextResponse.redirect(signInUrl); }

Convex-side identity enforcement is explicit in feature handlers.

const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error("Not authenticated"); } const userId = identity.subject; const tasks = await ctx.db .query("tasks") .withIndex("by_user", (q) => q.eq("userId", userId)) .collect();

Convex backend surface

  • Data model is centralized in convex/schema.ts.
  • Domain handlers are file-scoped (tasks.ts, notifications.ts, user.ts, features/*).
  • HTTP endpoints are registered in convex/http.ts for auth and billing web-facing routes.
const RATE_LIMITED_PATHS = [ "/api/auth/sign-in/email", "/api/auth/sign-up/email", "/api/auth/forget-password", "/api/auth/reset-password", ]; authComponent.registerRoutes(http, createAuth); polar.registerRoutes(http);
tasks: defineTable({ title: v.string(), status: v.union( v.literal("pending"), v.literal("in progress"), v.literal("completed"), v.literal("cancelled"), ), priority: v.union(v.literal("low"), v.literal("medium"), v.literal("high")), userId: v.string(), createdAt: v.number(), updatedAt: v.number(), }).index("by_user", ["userId"]);

Data flow examples

  • Task list read path: React client -> api.tasks.list query -> ctx.db.query("tasks").withIndex("by_user") -> filtered/sorted result.
  • Task completion side effect: tasks.update mutation -> status transition check -> ctx.scheduler.runAfter(...internal.notifications.createNotification).
  • Session bootstrap: Next.js server layout token fetch -> initialToken prop -> Convex auth-aware React provider.
Last updated on