Skip to content

Commit 4751b25

Browse files
perf(auth): replace getUser() with getSession() in 15 server/client components
These files needed the full User object (email, metadata) so they couldn't use getCurrentUserId(). Now use getSessionUser() which reads from the cookie (zero network calls) instead of getUser() (1 auth API call each). Safe because the middleware already validated the token via getUser() — subsequent reads within the same request don't need re-validation. Also adds getSessionUser() helper to lib/supabase/tenant.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent aa7bc8e commit 4751b25

16 files changed

Lines changed: 46 additions & 53 deletions

File tree

app/[locale]/(public)/checkout/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { PackageSearch, ArrowLeft } from "lucide-react";
66
import Link from "next/link";
77
import { Button } from "@/components/ui/button";
88
import { getCurrentTenantId } from "@/lib/supabase/tenant";
9+
import { getSessionUser } from '@/lib/supabase/tenant'
910

1011
interface SearchParams {
1112
courseId?: string;
@@ -19,8 +20,7 @@ export default async function CheckoutPage(props: { params: Promise<{ locale: st
1920
const t = await getTranslations('checkout');
2021

2122
const supabase = await createClient();
22-
const { data: { user } } = await supabase.auth.getUser();
23-
23+
const user = await getSessionUser()
2424
if (!user) {
2525
const returnUrl = encodeURIComponent(`/checkout?${courseId ? `courseId=${courseId}` : `planId=${planId}`}`);
2626
redirect(`/auth/login?next=${returnUrl}`);

app/[locale]/checkout/manual/page.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { createClient } from '@/lib/supabase/server'
21
import { redirect } from 'next/navigation'
32
import { getTranslations } from 'next-intl/server'
4-
import { getCurrentTenantId } from '@/lib/supabase/tenant'
3+
import { createClient } from '@/lib/supabase/server'
4+
import { getCurrentTenantId, getSessionUser } from '@/lib/supabase/tenant'
55
import { PaymentRequestForm } from '@/components/student/payment-request-form'
66
import { IconShieldCheck, IconLock } from '@tabler/icons-react'
77

@@ -18,15 +18,12 @@ export default async function ManualCheckoutPage(props: {
1818
const { locale } = await props.params
1919
const { productId, planId } = searchParams
2020
const t = await getTranslations('checkout.manual')
21-
22-
const supabase = await createClient()
2321
const tenantId = await getCurrentTenantId()
2422

25-
// Get authenticated user
26-
const {
27-
data: { user },
28-
} = await supabase.auth.getUser()
23+
const supabase = await createClient()
2924

25+
// Get authenticated user (no network call — reads from cookie)
26+
const user = await getSessionUser()
3027
if (!user) {
3128
const returnParam = productId ? `productId=${productId}` : `planId=${planId}`
3229
const returnUrl = encodeURIComponent(`/checkout/manual?${returnParam}`)

app/[locale]/create-school/page.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
import { createClient } from '@/lib/supabase/server'
21
import { CreateSchoolFlow } from '@/components/tenant/create-school-flow'
2+
import { getSessionUser } from '@/lib/supabase/tenant'
33

44
export default async function CreateSchoolPage() {
5-
const supabase = await createClient()
6-
const { data: { user }, error } = await supabase.auth.getUser()
7-
// If JWT is invalid/expired, treat as unauthenticated
8-
const validUser = error ? null : user
5+
const user = await getSessionUser()
96

107
return (
118
<div className="min-h-screen bg-[#0A0A0A] flex items-center justify-center p-4">
129
<div className="w-full max-w-lg">
1310
<CreateSchoolFlow
14-
user={validUser ? { id: validUser.id, email: validUser.email || '' } : null}
11+
user={user ? { id: user.id, email: user.email || '' } : null}
1512
/>
1613
</div>
1714
</div>

app/[locale]/dashboard/admin/enrollments/page.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getTranslations } from 'next-intl/server'
55
import { format } from 'date-fns'
66
import { es, enUS } from 'date-fns/locale'
77
import { getUserRole } from '@/lib/supabase/get-user-role'
8-
import { getCurrentTenantId } from '@/lib/supabase/tenant'
8+
import { getCurrentTenantId, getSessionUser } from '@/lib/supabase/tenant'
99
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
1010
import { Badge } from '@/components/ui/badge'
1111
import { Button } from '@/components/ui/button'
@@ -25,10 +25,7 @@ export default async function AdminEnrollmentsPage({
2525
const supabase = await createClient()
2626
const tenantId = await getCurrentTenantId()
2727

28-
const {
29-
data: { user },
30-
} = await supabase.auth.getUser()
31-
28+
const user = await getSessionUser()
3229
if (!user) {
3330
redirect('/auth/login')
3431
}

app/[locale]/dashboard/layout.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { SidebarProvider, SidebarTrigger, SidebarInset } from "@/components/ui/s
22
import { AppSidebar } from "@/components/app-sidebar"
33
import { Separator } from "@/components/ui/separator"
44
import { getUserRole } from "@/lib/supabase/get-user-role"
5-
import { createClient } from "@/lib/supabase/server"
5+
import { getSessionUser } from "@/lib/supabase/tenant"
66
import { ModeToggle } from "@/components/mode-toggle"
77
import { UserNav } from "@/components/user-nav"
88
import { LanguageSwitcher } from "@/components/language-switcher"
@@ -14,8 +14,7 @@ export default async function DashboardLayout({
1414
children: React.ReactNode
1515
}) {
1616
const role = await getUserRole()
17-
const supabase = await createClient()
18-
const { data: { user } } = await supabase.auth.getUser()
17+
const user = await getSessionUser()
1918

2019
return (
2120
<SidebarProvider>

app/[locale]/dashboard/student/courses/[courseId]/exams/[examId]/exam-taker.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ export function ExamTaker({
9292
setSubmitting(true)
9393

9494
try {
95-
const {
96-
data: { user },
97-
} = await supabase.auth.getUser()
95+
const { data: { session } } = await supabase.auth.getSession()
96+
const user = session?.user
9897

9998
if (!user) {
10099
router.push('/auth/login')

app/[locale]/dashboard/student/courses/[courseId]/lessons/[lessonId]/lesson-navigation.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ export function LessonNavigation({
5050
async function handleComplete() {
5151
setLoading(true)
5252

53-
const {
54-
data: { user },
55-
} = await supabase.auth.getUser()
53+
const { data: { session } } = await supabase.auth.getSession()
54+
const user = session?.user
5655

5756
if (!user) {
5857
setLoading(false)

app/[locale]/dashboard/student/page.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { createClient } from '@/lib/supabase/server'
21
import { redirect } from 'next/navigation'
2+
import { createClient } from '@/lib/supabase/server'
33
import { getTranslations } from 'next-intl/server'
44
import { WelcomeHero } from '@/components/student/welcome-hero'
55
import { StatsCards } from '@/components/student/stats-cards'
@@ -10,7 +10,7 @@ import { IconRocket, IconSparkles, IconCircleCheck } from '@tabler/icons-react'
1010
import { Button } from '@/components/ui/button'
1111
import Link from 'next/link'
1212
import { MiniLeaderboard } from '@/components/gamification/mini-leaderboard'
13-
import { getCurrentTenantId } from '@/lib/supabase/tenant'
13+
import { getCurrentTenantId, getSessionUser } from '@/lib/supabase/tenant'
1414
import { OnboardingChecklist } from '@/components/shared/onboarding-checklist'
1515
import { StudentDashboardTour } from '@/components/tours/student-dashboard-tour'
1616

@@ -105,10 +105,7 @@ async function getData(userId: string, tenantId: string) {
105105
export default async function StudentDashboard() {
106106
const tenantId = await getCurrentTenantId()
107107
const supabase = await createClient()
108-
const {
109-
data: { user },
110-
} = await supabase.auth.getUser()
111-
108+
const user = await getSessionUser()
112109
if (!user) {
113110
redirect('/auth/login')
114111
}

app/[locale]/dashboard/student/profile/page.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { createClient } from '@/lib/supabase/server'
21
import { redirect } from 'next/navigation'
32
import { getTranslations } from 'next-intl/server'
43
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
@@ -27,7 +26,8 @@ import { ProfileGamificationStats } from '@/components/gamification/profile-stat
2726
import Link from 'next/link'
2827
import Image from 'next/image'
2928
import { StudentCertificateCard } from '@/components/student/student-certificate-card'
30-
import { getCurrentTenantId } from '@/lib/supabase/tenant'
29+
import { getCurrentTenantId, getSessionUser } from '@/lib/supabase/tenant'
30+
import { createClient } from '@/lib/supabase/server'
3131

3232
async function getProfileData(userId: string, tenantId: string) {
3333
const supabase = await createClient()
@@ -258,8 +258,7 @@ function PurchasedCourseCard({ course: ec, labels }: { course: any; labels: { no
258258
export default async function ProfilePage() {
259259
const tenantId = await getCurrentTenantId()
260260
const supabase = await createClient()
261-
const { data: { user } } = await supabase.auth.getUser()
262-
261+
const user = await getSessionUser()
263262
if (!user) {
264263
redirect('/auth/login')
265264
}

app/[locale]/onboarding/page.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1+
import { getCurrentTenantId, getSessionUser } from '@/lib/supabase/tenant'
12
import { createClient } from '@/lib/supabase/server'
2-
import { getCurrentTenantId } from '@/lib/supabase/tenant'
33
import { redirect } from 'next/navigation'
44
import { getTranslations } from 'next-intl/server'
55
import OnboardingWizard from '@/components/onboarding/onboarding-wizard'
66

77
export default async function OnboardingPage() {
8-
const supabase = await createClient()
98
const t = await getTranslations('onboarding')
109

11-
const {
12-
data: { user },
13-
} = await supabase.auth.getUser()
14-
10+
const supabase = await createClient()
11+
const user = await getSessionUser()
1512
if (!user) {
1613
redirect('/auth/login')
1714
}

0 commit comments

Comments
 (0)