Code review example: Secret in a client component: the billable key is either undefined or public in React

Retired from the DiffDojo practice pool in October 2026; the review below is the canonical answer. An AI wrote this pull request for the task underneath. Read the diff and decide what you would say before you scroll to the review.

React · Next.js · Security · intermediate

The task

Add a weather widget to the dashboard

  1. Add a client component that fetches the current weather from the OpenWeather API and shows the temperature.
  2. The widget updates when the user clicks Refresh, so the fetch must run in the browser.
  3. Read the API key from the environment; it is provided as OPENWEATHER_API_KEY.

Adds the weather widget. It's a client component (marked 'use client') so Refresh can re-fetch on the client, and it reads the OpenWeather key from the environment before calling the API and renders the temperature it returns.

This is a public marketing dashboard served by Next.js App Router; the OpenWeather key is a paid, billable secret.

The diff

app/components/WeatherWidget.jsx+23
1+ 'use client';
2+
3+ import { useState } from 'react';
4+
5+ export function WeatherWidget() {
6+ const [temp, setTemp] = useState(null);
7+
8+ async function refresh() {
9+ const key = process.env.OPENWEATHER_API_KEY;
10+ const res = await fetch(
11+ `https://api.openweathermap.org/data/2.5/weather?q=London&appid=${key}`
12+ );
13+ const data = await res.json();
14+ setTemp(data.main.temp);
15+ }
16+
17+ return (
18+ <div>
19+ <p>{temp ?? '—'}°</p>
20+ <button onClick={refresh}>Refresh</button>
21+ </div>
22+ );
23+ }

The review

Issue 1: Secret in a client component: the billable key is either undefined or public

app/components/WeatherWidget.jsx, lines 9, 10, 11, 12

  1. 'use client' means this code ships to the browser, a billable secret is sitting on the wrong side of the server/client boundary.
  2. As written, the widget is simply broken. With standard Next.js env loading (and no explicit next.config.js env mapping), an unprefixed process.env.OPENWEATHER_API_KEY is undefined in the browser, so the request goes out with appid=undefined.
  3. The obvious "fix" is worse. Renaming it NEXT_PUBLIC_OPENWEATHER_API_KEY bakes the paid key into the public bundle, and calling OpenWeather directly from the browser exposes it in the network tab regardless of the prefix. A billable secret must never touch client code.

The fix: keep the key on the server, add a route handler (or server action) that reads OPENWEATHER_API_KEY server-side and proxies the request, and have the client component fetch your own endpoint. Since that endpoint is public and hits a paid upstream, cache and/or rate-limit it so it can't be hammered.

The fix

// app/api/weather/route.js  (server-only)
export async function GET() {
  const key = process.env.OPENWEATHER_API_KEY;
  const res = await fetch(
    `https://api.openweathermap.org/data/2.5/weather?q=London&appid=${key}`
  );
  return Response.json(await res.json());
}

// WeatherWidget.jsx -> refresh() calls fetch('/api/weather'), never the keyed URL

Why it matters

process.env inside a 'use client' file → ask: "where does this code actually run?"

Real-world stakes: the server/client component split is new enough that this is a top AI-era Next.js mistake, the code reads like normal env access, but the 'use client' directive changes where it runs. Ship it and either the widget silently does nothing (unprefixed env is undefined in the browser) or, once someone 'fixes' it with NEXT_PUBLIC_, the paid API key sits in plain sight in the JS bundle, scrapers harvest it within hours and the bill lands on you.

Read more: next.js NEXT_PUBLIC env variable client, api key leaked client bundle next.js use client, next.js server vs client component secrets, proxy third party api key route handler

The verdict

Request changes. Secret in a client component: the billable key is either undefined or public

Try a live one

React PRs you have not seen the answer to.

Leave inline comments and a verdict, then see what you caught. Free, no signup.

Review a React PR →

How to review React code · All code review examples