Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build CRUD in Next.js by first identifying the router. In the App Router, use Server Functions (often called Server Actions) for server-side mutations; in the Pages Router, submit to secured API Routes. In either case, treat every mutation as a server endpoint: authenticate the caller, authorize access to the specific record, validate and normalize server-side, persist through your data layer, and deliberately refresh affected data.

Choose the CRUD pattern that matches your router

Concern App Router Pages Router
Server-side mutation Server Functions/Actions invoked by forms or client code API Routes handle server-side form mutations
Input A Server Action receives FormData from a form An API handler reads request data according to its implementation
Cache handling Use revalidatePath or revalidateTag for affected cached data Follow the Pages Router and your Next.js version’s data-fetching behavior
Security Check authentication and authorization inside every action Secure each API endpoint and authorize the requested operation

These APIs are not interchangeable. Confirm the installed Next.js version and whether the project uses App Router mutation APIs or the Pages Router forms approach before copying an example.

A reliable App Router mutation sequence

For create, update, and delete operations, use the same ordered boundary:

  1. Render a form or otherwise collect the operation’s inputs.
  2. Receive the request in a server-side function.
  3. Authenticate the session.
  4. Authorize the operation against the target record, tenant, or ownership scope.
  5. Extract, validate, and normalize every expected value.
  6. Run the database operation in the application’s data layer.
  7. Return a useful validation or failure state when it cannot succeed.
  8. Invalidate affected paths or tags, then redirect if navigation is required.

The sequence follows the flow in Next.js’s mutating-data tutorial and its mutation guide. Next.js Server Functions execute on the server and are invoked through a network request; form actions receive FormData, and only POST requests can invoke them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Example: a server-side create action

'use server'

import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createRecord(formData: FormData) {
  const session = await requireSession()
  const name = String(formData.get('name') ?? '').trim()
  const amountText = String(formData.get('amount') ?? '')

  if (!name || !/^d+(.d{1,2})?$/.test(amountText)) {
    return { error: 'Enter a name and a valid amount.' }
  }

  const amount = Number(amountText)
  await db.record.create({
    data: { name, amount, ownerId: session.user.id }
  })

  revalidatePath('/records')
  redirect('/records')
}

The database client, schema, transaction policy, and error mapping are application choices. Add uniqueness constraints, transactions, and concurrency controls where the domain requires them; Next.js does not supply those guarantees automatically.

Forms, validation, and feedback

Use named controls and server validation

Read only the fields your action expects from FormData. Client-side required, type, and range attributes improve usability but are not access control or a sufficient validation layer: values cross a network boundary. Validate types, required fields, ranges, relationships, and business rules on the server before writing.

The Next.js Forms guide covers server-side validation, validation errors, pending states, and optimistic updates. Return field-level or form-level errors in a predictable shape so the UI can associate feedback with the failed submission.

Pending and optimistic states

Disable duplicate submissions and show progress while an action is pending. An optimistic update may make the interface appear immediate, but it is only a presentation pattern: the server mutation remains the durable authority, and the client must handle rejection or rollback when validation, authorization, or persistence fails.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Progressive enhancement

A form in a Server Component can submit before JavaScript loads. Forms in Client Components have different queuing and hydration behavior, as described in the mutation guide. Design the server response and error path to remain useful even when enhancement has not completed.

Secure every mutation, including update and delete

Next.js states: Always verify authentication and authorization inside every Server Function. Read the guidance in Getting Started: Mutating Data and Guides: Authentication.

  • Authenticate: establish which user or service made the request.
  • Authorize: confirm that identity may perform this operation.
  • Scope the record: for read, update, and delete, verify ownership, tenant membership, or another record-level policy in the database query or immediately before the write.
  • Do not rely on the UI: a hidden button, protected page, or client-side check can be bypassed because actions are reachable through direct POST requests.

For a delete action, accept an identifier only after checking that the current principal can delete that identifier. Avoid a broad “signed-in users can mutate” rule when records belong to different users or organizations.

Persist safely and handle failures

Keep database calls behind the server mutation boundary. Decide how the data layer handles uniqueness conflicts, missing records, transaction boundaries, retries, and concurrent edits. Map expected failures to user-readable validation or conflict messages; log unexpected failures without exposing sensitive database details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For multi-step writes, use the transaction facility of your selected database or ORM. The reviewed Next.js documentation does not prescribe a vendor, ORM, schema, deployment target, or transaction model, so those decisions must follow your application’s consistency and scaling requirements.

Make changed data appear fresh

Revalidate the data your page actually uses

After a successful write, invalidate the route or data tags that feed the affected views with revalidatePath or revalidateTag. If the page reads tagged data, use the corresponding tag; if the route’s rendered output is the relevant cache, revalidate its path. Perform revalidation before calling redirect, because redirect is control flow and statements after it do not run. See the mutation documentation.

Do not confuse refresh with invalidation

A client router refresh can request a new render, but it is not a substitute for invalidating tagged server data. Choose the invalidation method based on how the page’s data is cached and tagged. A successful mutation should therefore have an explicit freshness plan rather than relying on a navigation side effect.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Configuration and request limits

The Server Actions configuration reference documents same-origin Origin-versus-host checking as the default CSRF mitigation and supports additional trusted proxy domains through allowedOrigins. It also documents a default Server Action request-body limit of 1 MB, configurable with serverActions.bodySizeLimit. Treat that as a framework configuration default for the documented Next.js release, not as a performance benchmark; verify the setting in your installed version before accepting large forms.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Pages Router CRUD with API Routes

In a Pages Router project, post the form to an API Route (or call it from client code), parse and validate the request in that handler, authenticate and authorize there, perform the data-layer operation, and return an appropriate status and response. Keep the same security and persistence rules as an App Router action; only the transport and framework conventions change. Consult the Pages Router Forms guide for the version-specific form and API pattern.

CRUD readiness checklist

  • The example matches the project’s router and installed Next.js version.
  • Every mutation authenticates and authorizes inside the server boundary.
  • Record-level ownership or tenant checks protect reads, updates, and deletes.
  • All submitted values are extracted, normalized, and validated on the server.
  • Expected validation and persistence failures produce usable UI feedback.
  • Database constraints, transactions, and concurrency behavior match the domain.
  • Affected paths or tags are revalidated after successful writes.
  • Revalidation occurs before any redirect.
  • Client refresh behavior is not being mistaken for cache invalidation.
  • Request-size and trusted-origin settings are reviewed for the deployed version.

The Bottom Line

Perfect CRUD in Next.js is less about one scaffold and more about a disciplined boundary: select the router-appropriate API, secure and validate on the server, persist through a deliberate data layer, and revalidate the exact data your interface displays.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.