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
- Browser request enters Next.js.
src/proxy.tsperforms redirect and coarse auth/rate checks.- Route/layout renders; server layout fetches token with
getToken(). - Client components call Convex queries/mutations over
ConvexReactClient. - 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.tsfor 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.listquery ->ctx.db.query("tasks").withIndex("by_user")-> filtered/sorted result. - Task completion side effect:
tasks.updatemutation -> status transition check ->ctx.scheduler.runAfter(...internal.notifications.createNotification). - Session bootstrap: Next.js server layout token fetch ->
initialTokenprop -> Convex auth-aware React provider.
Last updated on