api-platform / create-client

Generate React or Vue.js-based Progressive Web Apps from an Hydra-enabled API. Also support React Native.
https://api-platform.com/docs/client-generator/
MIT License
369 stars 131 forks source link

Production build fails for nextjs client #378

Open JDruery opened 4 months ago

JDruery commented 4 months ago

API Platform version(s) affected: 3.2.16

Description
With a fresh installation, I was able to run the generator for the default Greeting entity. The app works in dev mode. When I try to build the app in production mode with http only, it fails with the following message:

9.052    Creating an optimized production build ...
38.73  ✓ Compiled successfully
38.73    Collecting page data ...
39.13 TypeError: Failed to parse URL from undefined/greetings
39.13     at node:internal/deps/undici/undici:12345:11
39.13     at async c (/srv/app/.next/server/pages/greetings/page/[page].js:1:7873)
39.13     at async c (/srv/app/.next/server/pages/greetings/page/[page].js:1:7119)
39.13     at async buildStaticPaths (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:786:33)
39.13     at async /srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:1215:110
39.13     at async Span.traceAsyncFn (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/trace/trace.js:151:20) {
39.13   [cause]: TypeError: Invalid URL
39.13       at new URL (node:internal/url:775:36)
39.13       at new Request (node:internal/deps/undici/undici:5853:25)
39.13       at fetch (node:internal/deps/undici/undici:10123:25)
39.13       at Object.fetch (node:internal/deps/undici/undici:12344:10)
39.13       at fetch (node:internal/process/pre_execution:336:27)
39.13       at c (/srv/app/.next/server/pages/greetings/page/[page].js:1:7892)
39.13       at c (/srv/app/.next/server/pages/greetings/page/[page].js:1:7133)
39.13       at buildStaticPaths (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:786:39)
39.13       at /srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:1215:116
39.13       at async Span.traceAsyncFn (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/trace/trace.js:151:20) {
39.13     code: 'ERR_INVALID_URL',
39.13     input: 'undefined/greetings'
39.13   }
39.13 }
39.13 TypeError: Failed to parse URL from undefined/greetings
39.13     at node:internal/deps/undici/undici:12345:11
39.13     at async o (/srv/app/.next/server/pages/greetings/[id].js:1:5312)
39.13     at async y (/srv/app/.next/server/pages/greetings/[id].js:1:4222)
39.13     at async buildStaticPaths (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:786:33)
39.13     at async /srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:1215:110
39.13     at async Span.traceAsyncFn (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/trace/trace.js:151:20) {
39.13   [cause]: TypeError: Invalid URL
39.13       at new URL (node:internal/url:775:36)
39.13       at new Request (node:internal/deps/undici/undici:5853:25)
39.13       at fetch (node:internal/deps/undici/undici:10123:25)
39.13       at Object.fetch (node:internal/deps/undici/undici:12344:10)
39.13       at fetch (node:internal/process/pre_execution:336:27)
39.13       at o (/srv/app/.next/server/pages/greetings/[id].js:1:5331)
39.13       at y (/srv/app/.next/server/pages/greetings/[id].js:1:4236)
39.13       at buildStaticPaths (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:786:39)
39.13       at /srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:1215:116
39.13       at async Span.traceAsyncFn (/srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/trace/trace.js:151:20) {
39.13     code: 'ERR_INVALID_URL',
39.13     input: 'undefined/greetings'
39.13   }
39.13 }
39.13
39.13 > Build error occurred
39.13 Error: Failed to collect page data for /greetings/page/[page]
39.13     at /srv/app/node_modules/.pnpm/next@14.1.2_@babel+core@7.24.0_react-dom@18.2.0_react@18.2.0/node_modules/next/dist/build/utils.js:1258:15 {
39.13   type: 'Error'
39.13 }
39.15  ELIFECYCLE  Command failed with exit code 1.
------
failed to solve: process "/bin/sh -c pnpm install --frozen-lockfile --offline --prod && \tpnpm run build" did not complete successfully: exit code: 1

How to reproduce

J3m5 commented 4 months ago

Hello @JDruery,

Thank you for reporting this issue. The problem you're encountering during the production build appears to be related to the Next.js client's inability to access the API, which is essential for server-side generation (SSG) during the build process. Here's how to resolve it:

1. Set the API Endpoint in the .env file

First, we need to ensure that the Next.js client knows where to find the API. Create a .env file in the pwa directory and add the following line:

NEXT_PUBLIC_ENTRYPOINT=http://php

This environment variable (NEXT_PUBLIC_ENTRYPOINT) tells the Next.js client the base URL of your API, enabling it to make requests during the build and runtime.

2. Configure Docker Compose for Network Access

In the compose.prod.yaml file, we'll adjust the pwa service to ensure it can communicate with the php service during the build. Here's the modification:

  pwa:
    build:
      context: ./pwa
      target: prod
      network: host
      extra_hosts:
        - "php=127.0.0.1"

These changes allow the pwa build process to access the php service by mapping the php hostname to the local machine, facilitating SSG.

3. Adjust Environment Variables for the PHP Service

For seamless operation of the API and Mercure Hub on a local setup without HTTPS, we need to tweak the php service configuration in the compose.yaml file. This adjustment ensures they can properly handle requests from localhost. The changes below include modifications to the environment variables, with the original settings preserved as comments for easy reference and reversal if needed:

  php:
    image: ${IMAGES_PREFIX:-}app-php
    depends_on:
      - database
    restart: unless-stopped
    environment:
      PWA_UPSTREAM: pwa:3000
      SERVER_NAME: ${SERVER_NAME:-localhost}, php:80
      MERCURE_PUBLISHER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!}
      MERCURE_SUBSCRIBER_JWT_KEY: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!}
      TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}
      # TRUSTED_HOSTS: ^${SERVER_NAME:-example\.com|localhost}|php$$
      TRUSTED_HOSTS: ^localhost|php$$
      DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-15}&charset=${POSTGRES_CHARSET:-utf8}
      MERCURE_URL: ${CADDY_MERCURE_URL:-http://php/.well-known/mercure}
      # MERCURE_PUBLIC_URL: https://${SERVER_NAME:-localhost}/.well-known/mercure
      MERCURE_PUBLIC_URL: http://localhost/.well-known/mercure
      MERCURE_JWT_SECRET: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!}

4. Build and start the php service

Let's start the php service to make it accessible:

SERVER_NAME=http://localhost APP_SECRET=!ChangeMe! CADDY_MERCURE_JWT_SECRET=ChangeThisMercureHubJWTSecretKey POSTGRES_PASSWORD=!ChangeMe! docker compose -f compose.yaml -f compose.prod.yaml up -d --build --wait php

5. Build the pwa service

docker compose -f compose.yaml -f compose.prod.yaml build pwa

6. Bring up the full project

SERVER_NAME=http://localhost  APP_SECRET=!ChangeMe! CADDY_MERCURE_JWT_SECRET=ChangeThisMercureHubJWTSecretKey POSTGRES_PASSWORD=!ChangeMe! docker compose -f compose.yaml -f compose.prod.yaml up -d --wait

With these adjustments, your production build should succeed, and the Next.js client will be able to access the API during the build process and at runtime.

J3m5 commented 4 months ago

I've updated my initial post due to additional issues encountered during runtime. For the API and Mercure to function correctly without HTTPS, it's necessary to revise the TRUSTED_HOSTS and MERCURE_PUBLIC_URL environment variables in the compose.yaml file.