Server-only Code

Keep Secrets on the Server

Modules that use secrets or the database must never be imported by a Client Component by mistake. Add import 'server-only' at the top: if a client file imports it, the build fails instead of leaking code.

Example

npm install server-only

Example output

added 1 package in 1s

lib/payments.ts

import 'server-only';

const RAZORPAY_SECRET = process.env.RAZORPAY_KEY_SECRET!;

export async function createPaymentOrder(amountPaise: number) {
  const auth = Buffer.from(process.env.RAZORPAY_KEY_ID + ':' + RAZORPAY_SECRET).toString('base64');
  const res = await fetch('https://api.razorpay.com/v1/orders', {
    method: 'POST',
    headers: { Authorization: 'Basic ' + auth, 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount: amountPaise, currency: 'INR' }),
  });
  return res.json();
}

The Build Error You Want

Example

If components/PayButton.tsx (a client file) imports lib/payments.ts:

npm run build

Example output

Error:   x 'server-only' cannot be imported from a Client Component module.
  | It should only be used from a Server Component.
Import trace for requested module:
  ./lib/payments.ts
  ./components/PayButton.tsx

Note: Environment variables without NEXT_PUBLIC_ are already undefined in the browser, but the code (URLs, logic) would still leak. server-only protects the whole module. There is also client-only for browser-only modules.