Skip to main content
Back to Read
Next.js Development26 September 20269 min read

Next.js App Architecture: Review the Boundaries Before Launch

An engineering review of a customer portal: trace one operation through data access, permissions, caching and recovery, then test the boundaries that matter.

A paper collage of stacked coloured bands in cream, slate blue and charcoal, with a torn paper arrow threading down through them and two small tags at the side.
Editorial illustration.

Review a Next.js app by following an important operation from the browser to the stored record and back. At each boundary, establish who the caller is, what they may do, which data leaves the server and what happens when the operation fails or repeats. A folder structure alone cannot answer those questions.

This guide is for developers, technical buyers and people reviewing a delivery proposal. It uses a fictional customer portal in which a customer approves the next step on a job. The design choices and test matrix are a worked review method, not a report of production results or a complete security audit.

If you are defining the product first, start with the business owner's Next.js app guide. The Next.js development page explains the wider delivery approach.

Establish the version and the operating model

Record the installed Next.js and React versions, router, runtime, hosting model and relevant configuration in the review. The router connects addresses to application views; the runtime is the environment that executes the server code. App Router guidance and Pages Router guidance are not interchangeable. Defaults and opt-in features change between releases; a code sample without that context is difficult to evaluate.

Sources in this guide were checked on 26 September 2026. The official Next.js 16.3 release and app-like experiences article introduce capabilities worth evaluating, including Instant Navigations. Their availability does not establish that a particular project's configuration, permissions or user journey is correct.

Use official Next.js announcements to identify changes, then read the relevant version's documentation and test the behaviour in your application. A release note is a starting point for evaluation, not evidence that an upgrade has already improved your product.

Draw the boundaries around one operation

  1. 01Validate the request
  2. 02Authorise access to the record
  3. 03Commit and confirm the result

For the fictional portal, the important operation is: a customer approves an open proposal attached to their job. The minimum model includes an account, a user with membership of that account, a job belonging to that account and a proposal belonging to that job.

The approval must refer to the proposal the customer actually reviewed. If staff change the proposal while the customer is reading it, the system needs a conflict rule rather than silently approving new terms.

BoundaryQuestion to answerEvidence to inspect
Browser to serverWhich values can the caller change?Submitted fields, route parameters and direct requests
Identity to permissionIs this user currently allowed to act on this record?Session verification and record-level access checks
Permission to writeCan the record change between the check and the update?Transaction or conditional-update behaviour
Stored record to browserWhich fields are safe and necessary to return?Response payload and component props
Database to connected serviceWhat if the notification fails after approval succeeds?Delivery state, retry policy and duplicate handling
Release to running systemCan the old and new application versions safely overlap?Schema compatibility and deployment/recovery plan

This table is an original review aid. Adapt it to the operation's actual consequences. A payment, invitation or deletion needs different acceptance conditions from a harmless display preference.

Keep the server and browser responsibilities explicit

In the App Router, pages and layouts are Server Components by default. Client Components add browser interaction such as state, event handlers and browser APIs. A Client Component can still participate in server-rendered HTML on the initial load; “client” does not mean it is never rendered on the server. See the official component guide.

For the example portal, keep record retrieval and permission decisions in server-side code. Send the approval control only the fields it needs to display and submit a decision. Passing a whole database record to a Client Component can expose fields the interface never displays.

A useful review question is: if someone inspects every response and changes every submitted identifier, does the server still enforce the same rules? Hiding a button or omitting a navigation link is an interface decision, not an access-control decision.

Put permission checks next to the data

A data access layer is server-side code that centralises how records are read and changed. The Next.js data-security guide recommends this approach for new projects, including authorisation and returning only safe, necessary data.

For a read, resolve the current user from a verified session, establish current account membership, then retrieve only a job they may access. Do not trust an account identifier simply because the browser supplied it. An identifier can help locate a record; it does not grant access to it.

Account membership alone is not enough when customers within the same account have different jobs. For a customer role, check the job belongs to that verified customer too. For staff and administrators, apply the specific role and assignment rules. Test both cross-account access and two customers inside the same account.

For a write, check the same relationship again at the operation's entry point. A Server Action is a server function called through the framework; a Route Handler implements an HTTP endpoint. Either can receive a request independently of the page that normally calls it. Shared layouts and early request checks are not sufficient protection for every entry point. The authentication guide explains why checks should happen close to the data source.

Define the response to an unauthorised record consistently. Depending on the product, that may be an access-denied response or one that avoids confirming whether the record exists. Test the chosen behaviour across detail pages, exports and mutation endpoints.

Make the approval safe to repeat and safe to reject

Here is the intended behaviour of the fictional operation, expressed as review steps rather than framework-specific code:

  1. Verify the session and validate the submitted proposal identifier and version.
  2. Resolve the user's current membership and permitted role from trusted server-side data.
  3. Locate the proposal through the authorised account, job and customer or staff assignment relationship.
  4. Check that the proposal is still open and its version matches the version reviewed.
  5. Commit the decision atomically: the version and open-state checks must still hold when the write happens.
  6. Return only the confirmation fields the interface needs.
  7. Record notification delivery separately so a retry cannot create a second approval.

An atomic change either completes as a whole or does not happen. The database and integration design must enforce that behaviour; a sequence of unrelated reads and writes is not equivalent.

An idempotent operation produces the same intended effect when a request is repeated. For this example, two submissions must not create two approvals. Decide how a client learns that its first request succeeded when the response was lost. For more consequential operations, such as payment, use the relevant provider's supported duplicate-protection mechanism and test reconciliation separately.

Optimistic UI shows the expected result before the server confirms it. If you use it, specify what happens when permission has been removed, a proposal changed or the write failed. The interface must recover honestly instead of displaying an approval that does not exist.

Treat caching as a data-sharing decision

Caching reuses a previous result. Before considering speed, ask whose result is being reused, who can receive it and how long it may be out of date.

InformationReview decisionTest worth running
Public help textReuse may be suitable if publishing refreshes it correctlyUpdate a paragraph and check the production page
A customer's job detailAccess and freshness rules must govern any reuseSwitch between two authorised accounts and inspect returned records
Current account membershipStale permissions could allow an action after access changesRemove membership, then repeat the operation
Approval stateThe confirmed write must reach the relevant viewsApprove once, refresh and open the staff view

Do not adopt a cache recipe from another Next.js version without checking its assumptions. Separate request-scoped reuse from data retained across requests, and check the actual cache configuration. A cache key containing a user ID is not a replacement for authorisation.

Keep private records out of public cache responses. Where authenticated caching is deliberately used, document scope, invalidation and permission-change handling. Review the current caching documentation against the installed version and feature configuration. The performance guide helps distinguish a slow operation from unnecessary work in the browser.

Design integrations around partial failure

Suppose the approval is stored but the email service is unavailable. The customer should not be told that approval failed if retrying would repeat a completed action. Separate business state from delivery state and give staff a way to detect and recover failed notifications.

For important delivery guarantees, assess a durable job or outbox: a stored record of work to send, committed consistently with the business change. Workers can then retry delivery with duplicate protection. This adds operational responsibility, so use it where the consequence of losing work justifies it.

Next.js can provide server endpoints, but deployment constraints still matter for long-running tasks, persistent connections and background work. The backend-for-frontend guide describes the role and limitations of that layer. Record what happens on timeout or process termination instead of assuming a task will finish because the HTTP response was sent.

Use a reproducible review matrix

Download the Next.js app review matrix (CSV). It contains proposed checks for this example, with blank actual-result and evidence fields. It is not a passed test report. Run it against a controlled environment with synthetic records and accounts.

For each check, record the application revision, configuration, environment, account role, exact action, expected result, observed result and evidence. Keep sensitive records out of screenshots and logs. Test both the normal interface and direct server entry points.

The most useful failures to find before launch include:

  • A customer can retrieve another account's record by changing its identifier.
  • A removed member can still mutate a record through a direct request.
  • Two concurrent approvals produce two effects or override a newer proposal.
  • A failed notification disguises a successful business operation.
  • A cache returns information from a previous user's session.
  • An old application instance cannot read data written by the new release.

Automate checks that would otherwise regress unnoticed. Browser tests establish user behaviour; server and database tests establish boundaries the interface may never expose. Neither set alone establishes complete security.

Review the release, not just the demonstration

Build and test the production configuration. Confirm required environment variables, secrets handling, logging, error reporting and recovery ownership. Do not include secrets or private records in the browser bundle or diagnostic output.

If a release changes the data model, plan compatibility across the old and new application versions. Rolling back application code does not automatically reverse a database migration. Test the restore or forward-fix procedure appropriate to that change.

For public routes, use the Next.js SEO checklist to check discovery and metadata. Private account content needs access control; a noindex directive is not protection. Use the official production guide as a companion to the app-specific checks.

A useful review ends with named owners, unresolved risks and evidence against the agreed acceptance conditions. Bring that record to the agency-selection discussion, or Get unstuck if you need to decide what the application should do next.

For reproducible content-freshness behaviour, use our CMS and caching experiment. The streaming guide separates response ordering from browser experience, while the production review takes the same boundaries into hosting, migration and recovery.

Focused first step

Clarity before complexity

Get unstuck

We start with a focused clarity chat so the report is based on your real bottlenecks, current situation, and commercial priorities.

Full digital presence audit
AI opportunity assessment
Custom growth roadmap
Report after your clarity chat
Get unstuck

The report is prepared after the chat if there is a sensible fit.