fix(ssr): résoudre une base absolue pour les fetch API côté serveur (page édition)

This commit is contained in:
devcodetools committed 2026-09-22 09:46:24 +02:00
1 parent 8ff530bf99
commit 0ca2846f2b
3 files changed
+58 -1

No files matched your search

+3
View File
@@ -1,6 +1,9 @@
# Variables exposées au client (préfixées NEXT_PUBLIC_)
# NEXT_PUBLIC_API_URL=http://localhost:3000
# Base interne pour les fetch SSR (port interne du serveur Next.js, défaut 3000)
# API_BASE_URL=http://localhost:3000
# Implémentation repository active : mysql | mongo
DB_IMPL=mysql
+48
View File
@@ -0,0 +1,48 @@
// @vitest-environment node
import { beforeEach, describe, expect, it, vi } from 'vitest'
const fetchMock = vi.fn()
async function loadModule(env?: Record<string, string>) {
vi.resetModules()
delete process.env.API_BASE_URL
if (env) Object.assign(process.env, env)
return (await import('./api-client')) as typeof import('./api-client')
}
beforeEach(() => {
global.fetch = fetchMock as unknown as typeof fetch
fetchMock.mockReset()
})
describe('cardsApi (SSR / Node)', () => {
it("résout une URL absolue sur localhost:3000 en l'absence de window", async () => {
const { cardsApi: api } = await loadModule()
fetchMock.mockResolvedValue(
new Response(JSON.stringify([]), {
status: 200,
headers: { 'Content-Type': 'application/json' },
}),
)
await api.list()
expect(fetchMock).toHaveBeenCalledWith(
'http://localhost:3000/api/cards',
expect.objectContaining({ method: 'GET' }),
)
})
it('respecte API_BASE_URL si elle est définie', async () => {
const { cardsApi: api } = await loadModule({ API_BASE_URL: 'http://api:9999' })
fetchMock.mockResolvedValue(
new Response(JSON.stringify([]), {
status: 200,
headers: { 'Content-Type': 'application/json' },
}),
)
await api.list()
expect(fetchMock).toHaveBeenCalledWith(
'http://api:9999/api/cards',
expect.objectContaining({ method: 'GET' }),
)
})
})
+7 -1
View File
@@ -14,10 +14,16 @@ interface ApiErrorPayload {
message?: string
}
// Côté serveur (SSR/standalone), une URL relative n'est pas résolvable :
// on cible la propre API de l'application (port interne 3000). Côté client,
// l'URL relative est résolue par le navigateur.
const API_BASE_URL =
typeof window === 'undefined' ? (process.env.API_BASE_URL ?? 'http://localhost:3000') : ''
async function request<T>(path: string, init?: RequestInit): Promise<T> {
let res: Response
try {
res = await fetch(path, {
res = await fetch(`${API_BASE_URL}${path}`, {
method: init?.method ?? 'GET',
...init,
headers: { 'Content-Type': 'application/json', ...init?.headers },