From bd222376bd98a5cff5f7db691a68df2080c6f0c5 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Sun, 22 Feb 2026 03:56:02 +0000 Subject: [PATCH] feat: add Strapi API client and data fetching functions Co-Authored-By: Claude Opus 4.6 --- apps/web/.env.example | 3 ++ apps/web/src/lib/api.ts | 56 ++++++++++++++++++++++++++++++++++++++ apps/web/src/lib/strapi.ts | 35 ++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 apps/web/.env.example create mode 100644 apps/web/src/lib/api.ts create mode 100644 apps/web/src/lib/strapi.ts diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..a779ce2 --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1,3 @@ +STRAPI_URL=http://localhost:1337 +STRAPI_API_TOKEN=your-api-token-here +NEXT_PUBLIC_STRAPI_URL=http://localhost:1337 diff --git a/apps/web/src/lib/api.ts b/apps/web/src/lib/api.ts new file mode 100644 index 0000000..3cc8aec --- /dev/null +++ b/apps/web/src/lib/api.ts @@ -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> { + return strapiGet({ + path: "/games", + locale, + params: { + "populate[coverImage]": "*", + "populate[documentary]": "*", + "sort": "createdAt:desc", + }, + }); +} + +export async function getGameBySlug(slug: string, locale: string): Promise> { + 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 { + 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> { + return strapiGet({ + path: `/chapters/${chapterId}`, + locale, + params: { + "populate[audioFile]": "*", + "populate[coverImage]": "*", + }, + }); +} diff --git a/apps/web/src/lib/strapi.ts b/apps/web/src/lib/strapi.ts new file mode 100644 index 0000000..d47c3e7 --- /dev/null +++ b/apps/web/src/lib/strapi.ts @@ -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; + locale?: string; +} + +export async function strapiGet({ path, params, locale }: FetchOptions): Promise { + 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(); +}