feat: add Strapi API client and data fetching functions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
3
apps/web/.env.example
Normal file
3
apps/web/.env.example
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
STRAPI_URL=http://localhost:1337
|
||||||
|
STRAPI_API_TOKEN=your-api-token-here
|
||||||
|
NEXT_PUBLIC_STRAPI_URL=http://localhost:1337
|
||||||
56
apps/web/src/lib/api.ts
Normal file
56
apps/web/src/lib/api.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import type {
|
||||||
|
Game,
|
||||||
|
Documentary,
|
||||||
|
Chapter,
|
||||||
|
StrapiListResponse,
|
||||||
|
StrapiResponse,
|
||||||
|
} from "@afterlife/shared";
|
||||||
|
import { strapiGet } from "./strapi";
|
||||||
|
|
||||||
|
export async function getGames(locale: string): Promise<StrapiListResponse<Game>> {
|
||||||
|
return strapiGet({
|
||||||
|
path: "/games",
|
||||||
|
locale,
|
||||||
|
params: {
|
||||||
|
"populate[coverImage]": "*",
|
||||||
|
"populate[documentary]": "*",
|
||||||
|
"sort": "createdAt:desc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGameBySlug(slug: string, locale: string): Promise<StrapiResponse<Game>> {
|
||||||
|
return strapiGet({
|
||||||
|
path: "/games",
|
||||||
|
locale,
|
||||||
|
params: {
|
||||||
|
"filters[slug][$eq]": slug,
|
||||||
|
"populate[coverImage]": "*",
|
||||||
|
"populate[screenshots]": "*",
|
||||||
|
"populate[documentary][populate][chapters][populate]": "*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDocumentaryByGameSlug(
|
||||||
|
slug: string,
|
||||||
|
locale: string
|
||||||
|
): Promise<Documentary | null> {
|
||||||
|
const gameRes = await getGameBySlug(slug, locale);
|
||||||
|
const game = Array.isArray(gameRes.data) ? gameRes.data[0] : gameRes.data;
|
||||||
|
return game?.documentary ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getChapter(
|
||||||
|
chapterId: number,
|
||||||
|
locale: string
|
||||||
|
): Promise<StrapiResponse<Chapter>> {
|
||||||
|
return strapiGet({
|
||||||
|
path: `/chapters/${chapterId}`,
|
||||||
|
locale,
|
||||||
|
params: {
|
||||||
|
"populate[audioFile]": "*",
|
||||||
|
"populate[coverImage]": "*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
35
apps/web/src/lib/strapi.ts
Normal file
35
apps/web/src/lib/strapi.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
const STRAPI_URL = process.env.STRAPI_URL || "http://localhost:1337";
|
||||||
|
const STRAPI_TOKEN = process.env.STRAPI_API_TOKEN;
|
||||||
|
|
||||||
|
interface FetchOptions {
|
||||||
|
path: string;
|
||||||
|
params?: Record<string, string>;
|
||||||
|
locale?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function strapiGet<T>({ path, params, locale }: FetchOptions): Promise<T> {
|
||||||
|
const url = new URL(`/api${path}`, STRAPI_URL);
|
||||||
|
|
||||||
|
if (locale) url.searchParams.set("locale", locale);
|
||||||
|
if (params) {
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
url.searchParams.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers: HeadersInit = { "Content-Type": "application/json" };
|
||||||
|
if (STRAPI_TOKEN) {
|
||||||
|
headers.Authorization = `Bearer ${STRAPI_TOKEN}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(url.toString(), {
|
||||||
|
headers,
|
||||||
|
next: { revalidate: 60 },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Strapi error: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user