perf: use getSession() instead of getUser() in client components

Major performance improvement. Every client component that needed the
current user was calling supabase.auth.getUser(), which makes a
network round-trip to Supabase to validate the JWT. On a page that
fetches data conditional on the user, the flow was:

  Page mount
    -> getUser() [network ~150-400ms]
    -> query data [network ~150-400ms]
    -> render

That's 2 serial round-trips before the page renders.

supabase.auth.getSession() reads the session from localStorage with
NO network call. Using it in client code cuts per-page load by one
full round-trip (~150-400ms depending on region latency).

Replaced in 10 client files:
- src/app/(dashboard)/inbox/page.tsx
- src/app/(dashboard)/pipelines/page.tsx
- src/components/contacts/contact-detail-view.tsx
- src/components/contacts/contact-form.tsx
- src/components/contacts/import-modal.tsx
- src/components/inbox/contact-sidebar.tsx
- src/components/pipelines/deal-form.tsx
- src/components/pipelines/pipeline-settings.tsx
- src/components/settings/tag-manager.tsx
- src/components/settings/template-manager.tsx
- src/components/settings/whatsapp-config.tsx

NOT changed (correctly continue using getUser):
- src/middleware.ts (server-side, needs JWT validation)
- src/app/api/whatsapp/*/route.ts (server-side, security-critical)
- src/hooks/use-auth.ts (PR #4 handles this file)

Security note: getSession() trusts the client-stored session. For
API routes and middleware that make authorization decisions, we still
use getUser() which verifies with Supabase. Client components only
use the session's user ID to filter RLS-protected queries — the RLS
on the database is the actual authorization boundary, so a forged
user ID in the browser wouldn't bypass server-side policies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arnas Donauskas 2026-04-16 20:05:00 +03:00
parent 658d070811
commit 2e7d7a6282
No known key found for this signature in database
11 changed files with 30 additions and 22 deletions

View File

@ -25,8 +25,9 @@ export default function InboxPage() {
const checkConnection = async () => {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user) return;

View File

@ -85,8 +85,9 @@ export default function PipelinesPage() {
setCreating(true);
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user) {
setCreating(false);
return;

View File

@ -223,8 +223,9 @@ export function ContactDetailView({
setSavingNote(true);
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user) {
toast.error('Not authenticated');
setSavingNote(false);

View File

@ -87,8 +87,9 @@ export function ContactForm({
try {
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user) throw new Error('Not authenticated');
let contactId = contact?.id;

View File

@ -123,8 +123,9 @@ export function ImportModal({ open, onOpenChange, onImported }: ImportModalProps
try {
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user) throw new Error('Not authenticated');
let imported = 0;

View File

@ -84,8 +84,9 @@ export function ContactSidebar({ contact }: ContactSidebarProps) {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
const { data, error } = await supabase
.from("contact_notes")

View File

@ -96,8 +96,9 @@ export function DealForm({
await supabase.from("deals").update(payload).eq("id", deal.id);
} else {
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (user) {
await supabase
.from("deals")

View File

@ -92,8 +92,9 @@ export function PipelineSettings({
async function handleAddStage() {
if (!newStageName.trim()) return;
const {
data: { user },
} = await supabase.auth.getUser();
data: { session },
} = await supabase.auth.getSession();
const user = session?.user;
if (!user) return;
const { data } = await supabase

View File

@ -49,7 +49,7 @@ export function TagManager() {
async function fetchTags() {
try {
setLoading(true);
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession(); const user = session?.user;
if (!user) return;
const { data, error } = await supabase
@ -76,7 +76,7 @@ export function TagManager() {
try {
setSaving(true);
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession(); const user = session?.user;
if (!user) {
toast.error('Not authenticated');
return;

View File

@ -77,7 +77,7 @@ export function TemplateManager() {
async function fetchTemplates() {
try {
setLoading(true);
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession(); const user = session?.user;
if (!user) return;
const { data, error } = await supabase
@ -108,7 +108,7 @@ export function TemplateManager() {
try {
setSaving(true);
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession(); const user = session?.user;
if (!user) {
toast.error('Not authenticated');
return;

View File

@ -56,7 +56,7 @@ export function WhatsAppConfig() {
async function fetchConfig() {
try {
setLoading(true);
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession(); const user = session?.user;
if (!user) return;
const { data, error } = await supabase
@ -97,7 +97,7 @@ export function WhatsAppConfig() {
try {
setSaving(true);
const { data: { user } } = await supabase.auth.getUser();
const { data: { session } } = await supabase.auth.getSession(); const user = session?.user;
if (!user) {
toast.error('Not authenticated');
return;