Client import graph
Trace imports into files marked `"use client"`.
Treat a database connection string used through a `NEXT_PUBLIC_` variable in a Client Component as browser-visible. Stop direct browser database calls, move queries to a server route or server-only module, rotate the connection credential, and check what that credential could access. Changing the variable name alone does not remove a value from an already published JavaScript bundle.
This guide covers check 03: Secrets and key management in the Zenveus Production Readiness Standard.
For builders
A database connection string is a credential, not a public setting. Next.js can put a NEXT_PUBLIC_ value into JavaScript that anyone can download. Rotate the exposed credential and move database queries behind server-side authorization.
A scoped repair request
Use this prompt in Lovable, Cursor, Replit, or Claude Code with the relevant server files available.
Audit my Next.js client import graph for database credentials and NEXT_PUBLIC_DATABASE_URL. Move database access to server-only modules and authorized handlers, rotate exposed credentials, rebuild, and scan the served production bundle without printing secret values. Work on a branch with synthetic data and mocked external services. Show the smallest diff, identify required adapters and deployment settings, and add allowed and denied tests that prove side effects cannot happen before checks pass. Do not disable security checks to make a test pass.The same failure may appear as
Find the failure layer
Each check removes a class of causes. Keep the first failing result, its timestamp, and the production log beside it.
Trace imports into files marked `"use client"`.
Confirm whether the credential was actually configured for that build.
Determine whether it can read or write user records, and whether row-level rules apply to the connection type.
Ranked diagnosis
NEXT_PUBLIC variables can be inlined into client JavaScript at build time.
A Client Component imports code that initializes a direct database connection.
Removing source references does not revoke credentials already delivered to browsers.
UI login cannot control direct use of a browser-visible connection string.
Step-by-step repair
Edit the database helper (for example utils/db.ts), its Client Component imports, and the replacement server handler. Replace NEXT_PUBLIC_DATABASE_URL with a server-only variable.
Production safety ruleNever disable access controls, expose service keys, or add wildcard CORS as a routine shortcut.
Create a replacement credential with only the required privileges, switch the server to it, then revoke the exposed credential. Where exposure is active, contain access immediately. Review connection and query logs for unexpected use.
Add import server-only to the module that reads DATABASE_URL. Remove imports of that module from use client files. Never return the credential or a database object through props or API responses.
Read identity from your verified session, then query only records that identity may access. Return a small response object rather than the raw database record.
Remove the old NEXT_PUBLIC variable, rebuild, and deploy. Search served JavaScript and source maps for the retired variable and credential locally without printing secret values. Clear stale caches, but rely on revocation to neutralize copies of old bundles.
Implementation example
// lib/db.ts -- imported only by server code
import 'server-only';
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) throw new Error('DATABASE_URL is missing');
// Initialize your existing database driver with databaseUrl here.
// A Client Component calls an authorized HTTP route instead:
const response = await fetch('/api/my-content', { cache: 'no-store' });
if (!response.ok) throw new Error('Unable to load content');
const content = await response.json();The server route must perform session and record authorization. Changing the environment-variable prefix does not revoke an old credential or remove already-distributed bundles.
Prove the repair
Run these checks with synthetic data in your test environment, then repeat the relevant acceptance checks after deployment.
When the built-in AI fix makes it worse
Pause generated changes, restore a known working branch, and capture one failing request with its logs. Change one layer and rerun the allowed and denied checks before proceeding.
Engineering handoff
We trace one production request through the complete path, isolate the failing boundary, and leave behind evidence your team can repeat.
Public-prefixed configuration and database modules imported by client components.
Served builds, old artifacts, database role grants, and access logs.
The new authorized data handler and owner or tenant constraints.
Credential absence in deployed JavaScript and failed use of the revoked secret.
Typical repair pattern
The published bundle contains no connection secret and server routes deny unauthorized record access.
Before the next release
Clear answers
No. Public identifiers are appropriate there. A credential-bearing database URL is different because it grants database access.
No. A browser-visible URL can be used outside your UI. Enforce access on the server and at the database.
No. Rotate the credential and replace published bundles as part of the fix.
Run the verification checks on this page against your test environment, then repeat the relevant checks after deployment. Example code needs your app's authentication, data model, and configuration; reading the guide alone does not verify your deployment.
Related symptoms
Official documentation and library references
Free next step
The free tool helps you inspect this symptom. Its result does not establish whether the whole app is production ready.
The Verdict
We can see the symptom from here. What we cannot tell you from outside is whether it is contained or structural. A scanner collects evidence. A named senior engineer makes the decision. For $299, a named senior engineer reads your code and signs a written Verdict against the nine checks in the Zenveus Production Readiness Standard. The 48-hour clock begins when the required access and context are available. If the report does not give your developer a list they can act on, you do not pay.
The 48-hour clock starts when the required access and context are available.
Free decision aid
Get a senior view of the constraint, the evidence you have, and the next decision that removes the most risk.
No email required for this decision aid. Dismiss once and this popup stays closed for the session.