Skip to content

Commit 68aeaa2

Browse files
committed
perf: optimize serverless execution, edge caching, and locale hydration
1 parent 2e1e62e commit 68aeaa2

9 files changed

Lines changed: 58 additions & 29 deletions

File tree

‎src/app/api/leaderboard/route.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ export async function GET(request: Request) {
1616

1717
try {
1818
const result = await getLeaderboardResult(country);
19-
return NextResponse.json({ success: true, ...result });
19+
return NextResponse.json(
20+
{ success: true, ...result },
21+
{
22+
headers: {
23+
"Cache-Control": "public, s-maxage=3600, stale-while-revalidate=86400",
24+
},
25+
},
26+
);
2027
} catch (err) {
2128
console.error("Leaderboard DB query failed:", err);
2229

‎src/app/api/user/[username]/route.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ export async function GET(request: Request, { params }: { params: Promise<{ user
2020

2121
try {
2222
const { user, location } = await getUserProfile(trimmed, selectedLanguages);
23-
return NextResponse.json({ success: true, user, location });
23+
return NextResponse.json(
24+
{ success: true, user, location },
25+
{
26+
headers: {
27+
"Cache-Control": "public, s-maxage=1800, stale-while-revalidate=86400",
28+
},
29+
},
30+
);
2431
} catch (error: unknown) {
2532
console.error("User profile fetch error:", error);
2633
return formatApiErrorResponse(error);

‎src/app/layout.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export const metadata: Metadata = {
9999
export default async function RootLayout({ children }: { children: ReactNode }) {
100100
const cookieStore = await cookies();
101101
const headerStore = await headers();
102-
const cookieLocale = cookieStore.get(LOCALE_COOKIE)?.value;
102+
const cookieLocale = cookieStore.get(LOCALE_COOKIE)?.value || headerStore.get("x-locale");
103103
const initialLocale = isSupportedLocale(cookieLocale)
104104
? cookieLocale
105105
: parseAcceptLanguage(headerStore.get("accept-language"), supportedLocales, DEFAULT_LOCALE);

‎src/app/leaderboard/[country]/page.tsx‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,13 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
9191
};
9292
}
9393

94+
export const revalidate = 3600; // Cache on Vercel Edge CDN for 1 hour via ISR
95+
export const dynamicParams = true;
96+
9497
export async function generateStaticParams() {
95-
return countries.map((country) => ({ country: country.slug }));
98+
// Pre-render the top 20 most visited countries at build time.
99+
// Other countries are generated on-demand upon first visit and cached by ISR.
100+
return countries.slice(0, 20).map((country) => ({ country: country.slug }));
96101
}
97102

98103
export default async function CountryLeaderboardPage({ params }: Props) {

‎src/app/scoring-methodology/page.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Suspense } from "react";
12
import type { Metadata } from "next";
23
import { JsonLd } from "@/components/seo/json-ld";
34
import { ScoringMethodologyPageClient } from "@/features/scoring";
@@ -68,7 +69,9 @@ export default function ScoringMethodologyPage() {
6869
return (
6970
<>
7071
<JsonLd data={faqSchema} />
71-
<ScoringMethodologyPageClient />
72+
<Suspense fallback={<div className="min-h-screen" />}>
73+
<ScoringMethodologyPageClient />
74+
</Suspense>
7275
</>
7376
);
7477
}

‎src/app/user/[username]/page.tsx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ type CountryInfo = {
2121

2222
const countries = countriesData as CountryInfo[];
2323

24+
export const revalidate = 1800; // Cache user profile on Edge CDN for 30 minutes via ISR
25+
export const dynamicParams = true;
26+
2427
type Props = {
2528
params: Promise<{ username: string }>;
2629
searchParams?: Promise<{ country?: string }>;

‎src/lib/db/db-store.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ function getPoolConfig(): PoolConfig {
6767
);
6868
}
6969

70+
const isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME);
71+
7072
return {
7173
connectionString,
72-
max: 10,
73-
idleTimeoutMillis: 30_000,
74+
max: isServerless ? 2 : 10,
75+
idleTimeoutMillis: isServerless ? 10_000 : 30_000,
7476
connectionTimeoutMillis: 5_000,
7577
};
7678
}

‎src/lib/i18n/provider-hook.ts‎

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,7 @@
33
import { useCallback, useEffect, useMemo, useState } from "react";
44
import arMessages from "@/locales/ar.json";
55
import enMessages from "@/locales/en.json";
6-
import {
7-
DEFAULT_LOCALE,
8-
LOCALE_COOKIE,
9-
isSupportedLocale,
10-
localeMeta,
11-
supportedLocales,
12-
type Locale,
13-
} from "./core";
6+
import { DEFAULT_LOCALE, LOCALE_COOKIE, localeMeta, supportedLocales, type Locale } from "./core";
147

158
type Messages = Record<string, string>;
169

@@ -32,17 +25,8 @@ function persistLocale(locale: Locale) {
3225
}
3326

3427
export function useI18nProvider(initialLocale: Locale = DEFAULT_LOCALE) {
35-
const getInitialLocale = () => {
36-
if (typeof window !== "undefined") {
37-
const stored = window.localStorage.getItem(LOCALE_COOKIE);
38-
if (isSupportedLocale(stored)) return stored;
39-
}
40-
return initialLocale;
41-
};
42-
43-
const initialLoc = getInitialLocale();
44-
const [locale, setLocaleState] = useState<Locale>(initialLoc);
45-
const [messages, setMessages] = useState<Messages>(() => messagesByLocale[initialLoc]);
28+
const [locale, setLocaleState] = useState<Locale>(initialLocale);
29+
const [messages, setMessages] = useState<Messages>(() => messagesByLocale[initialLocale]);
4630
const [ready, setReady] = useState<boolean>(true);
4731

4832
const changeLocale = useCallback((next: Locale) => {

‎src/middleware.ts‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
} from "@/lib/i18n/core";
1010

1111
export function middleware(request: NextRequest) {
12-
const response = NextResponse.next();
12+
const requestHeaders = new Headers(request.headers);
1313
const cookieLocale = request.cookies.get(LOCALE_COOKIE)?.value;
1414

1515
if (!isSupportedLocale(cookieLocale)) {
@@ -18,11 +18,29 @@ export function middleware(request: NextRequest) {
1818
supportedLocales,
1919
DEFAULT_LOCALE,
2020
);
21+
requestHeaders.set("x-locale", locale);
2122

22-
response.cookies.set(LOCALE_COOKIE, locale, { path: "/" });
23+
const response = NextResponse.next({
24+
request: {
25+
headers: requestHeaders,
26+
},
27+
});
28+
29+
response.cookies.set(LOCALE_COOKIE, locale, {
30+
path: "/",
31+
maxAge: 60 * 60 * 24 * 365,
32+
sameSite: "lax",
33+
});
34+
35+
return response;
2336
}
2437

25-
return response;
38+
requestHeaders.set("x-locale", cookieLocale);
39+
return NextResponse.next({
40+
request: {
41+
headers: requestHeaders,
42+
},
43+
});
2644
}
2745

2846
// Run only on routes that produce HTML or read the cookie. Skip Next.js

0 commit comments

Comments
 (0)