Secrets 'undefined' - Build error Next.js App

Hello everyone,

this is my first time posting and I only have a few months experience in web development and deployment. I will try to provide all the necessary information and use the right words ;). Also, I checked the other related posts several times and could not find an answer to my problem yet (e.g. Build Time Secrets and Cannot read secrets from environment variable (Golang)).

So here it goes:

I am working on a blog for a friend in Next.js that has a ghost backend (deployment worked perfectly fine) and an extra frontend page, that I need because she wants the blog posts to appear in a specific way. So we cannot use the provided templates by ghost. This frontend website is the one, I could not deploy yet. It fails during the build phase and shows the following error:

#10 46.29 Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
#10 46.29 TypeError: Failed to parse URL from undefined/ghost/api/v3/content/posts/?key=undefined&fields=title,slug,html&include=tags
#10 46.29     at Object.fetch (node:internal/deps/undici/undici:11118:11)
#10 46.29     at async getPosts (/app/.next/server/pages/index.js:33:17)
#10 46.29     at async getStaticProps (/app/.next/server/pages/index.js:38:19)
#10 46.29     at async renderToHTML (/app/node_modules/next/dist/server/render.js:385:20)
#10 46.29     at async /app/node_modules/next/dist/export/worker.js:277:36
#10 46.29     at async Span.traceAsyncFn (/app/node_modules/next/dist/trace/trace.js:79:20)
#10 46.29 info  - Generating static pages (1/6)
#10 46.29 (node:114) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
#10 46.29 (Use `node --trace-warnings ...` to show where the warning was created)
#10 46.36 info  - Generating static pages (2/6)
#10 46.38 info  - Generating static pages (4/6)
#10 46.42 info  - Generating static pages (6/6)
#10 46.42
#10 46.43 > Build error occurred

The problem seems to be the environment variables, which I have tried to 1) set via flyctl secrets set BLOG_URL=xyz and 2) tried to set via flyctl deploy --build-secret BLOG_URL=xyz. They don’t reach my fetch request undefined/ghost/api/v3/content/posts/?key=undefined&fields=title,slug,html&include=tags. The function looks like this:

async function getPosts() {
  const res = await fetch(
    `${process.env.BLOG_URL}/ghost/api/v3/content/posts/?key=${process.env.CONTENT_API_KEY}&fields=title,slug,html&include=tags`,
  ).then((resp) => resp.json());

  const posts = res.posts;

  return posts;
}

The secrets exist when I run flyctl secrets list.

My Dockerfile looks like this:

# Initialize builder layer
FROM node:18-alpine AS builder
ENV NODE_ENV production
# Install necessary tools
RUN apk add --no-cache libc6-compat yq --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community
WORKDIR /app
# Copy the content of the project to the machine
COPY . .
RUN yq --inplace --output-format=json '.dependencies = .dependencies * (.devDependencies | to_entries | map(select(.key | test("^(typescript|@types/*|@upleveled/)"))) | from_entries)' package.json
RUN --mount=type=secret,id=BLOG_URL \
    BLOG_URL="$(cat /run/secrets/BLOG_URL)"
RUN --mount=type=secret,id=CONTENT_API_KEY \
    CONTENT_API_KEY="$(cat /run/secrets/CONTENT_API_KEY)"
RUN yarn install --frozen-lockfile
RUN yarn build

# Initialize runner layer
FROM node:18-alpine AS runner
ENV NODE_ENV production

# Copy built app
COPY --from=builder /app/.next ./.next

# Copy only necessary files to run the app (minimize production app size, improve performance)
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./
COPY --from=builder /app/.env.production ./

CMD ["yarn", "start"]

I don’t know what is going on and why neither the build secrets nor the runtime secrets change anything about my ‘undefined’ URL.

I appreciate any hints and help!

Answering as the same person but different user: This is from my friend’s account, for whom I am deploying the website. It is not another personal account but I have admin rights.

SOLUTION / WORKAROUND

I could find the solution with the help of a friend. The issue could be solved in a part of the code, that I did not post yet:

export const getServerSideProps = async () => {
  const posts = await getPosts();
  if (typeof posts === 'undefined') {
    return {
      props: {
        error: 'Nothing to see here',
      },
    };
  }
  return {
    props: { posts },
  };
};

In this piece of code, I had used getStaticProps’ which needs the variables during build. By using getServerSideProps, the runtime env variables are enough.

So, now I do not use any docker secrets, neither during deployment nor in my Dockerfile, only Fly runtime secrets.

:boom: