With the rise of Server Components in Next.js, traditional assumptions about frontend data access and security need to be reconsidered. This guide outlines secure data-fetching strategies and best practices to prevent accidental exposure of sensitive information.
There are three main approaches:
Mixing these approaches is discouraged to maintain clarity and security consistency.
In existing projects, you can fetch data from REST or GraphQL APIs inside Server Components:
const token = cookies().get('AUTH_TOKEN')?.value
const res = await fetch('https://api.example.com/profile', {
headers: {
Cookie: `AUTH_TOKEN=${token}`,
},
})This works well when backend teams operate independently or existing security policies are in place.
For new projects, a DAL centralizes data access logic and improves security:
Example:
export const getCurrentUser = cache(async () => {
const token = cookies().get('AUTH_TOKEN')
const decoded = await decryptAndValidate(token)
return new User(decoded.id)
})export async function getProfileDTO(slug: string) {
const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`
const userData = rows[0]
const currentUser = await getCurrentUser()
return {
username: canSeeUsername(currentUser) ? userData.username : null,
phonenumber: canSeePhoneNumber(currentUser, userData.team)
? userData.phonenumber
: null,
}
}For fast iteration, you might fetch data directly in Server Components. But this risks exposing sensitive data to the client:
// BAD: exposes full userData to client
return <Profile user={userData} />Better approach:
export async function getUser(slug: string) {
const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`
const user = rows[0]
return { name: user.name }
}On initial load, both Server and Client Components run on the server but in isolated module systems:
React provides experimental APIs to mark sensitive data:
experimental_taintObjectReferenceexperimental_taintUniqueValueEnable in next.config.js:
module.exports = {
experimental: {
taint: true,
},
}NEXT_PUBLIC_server-only to prevent server code from running on the client// lib/data.ts
import 'server-only'Data security in Next.js requires thoughtful design of access layers, clear separation between server and client environments, and use of protective tools like DAL, taint, and server-only. By following these practices, you can build secure, scalable, and trustworthy applications.