I have a database of names and want to provide a search field for this. The approach I'm used to (and what I've started with) from other environments is to expose a REST API with a search query parameter. This approach definitely works, but I'm not sure if it fits the Next philosophy. Should I instead be using a server action for this, and only use route handlers for recieveing data from external services? Are there downsides to either approach?
#Route handler vs server actions for searching a database
1 messages · Page 1 of 1 (latest)
Hi @oblique pollen. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
For your use case—searching a database of names via a search field—the most Next.js-aligned approach is to use a Route Handler (API endpoint) for the search functionality, rather than a Server Action.
Why Route Handlers are Preferred for Data Fetching
-
Direct Data Fetching: Next.js recommends fetching data in Server Components directly from its source (e.g., your database or an external API), not via Route Handlers, unless you specifically need to expose a public API endpoint. However, for client-side search (e.g., as the user types), a Route Handler is the right fit, as it allows you to expose a REST-like API endpoint that your client-side code can call with query parameters (e.g.,
/api/search?query=abc) How to use Next.js as a backend for your frontend. -
RESTful Pattern: The pattern of exposing a REST API with query parameters is fully supported and idiomatic in Next.js. You can easily access query parameters in your Route Handler using the
NextRequestobject Route Handler: URL Query Parameters. -
Performance: For Server Components, fetching data via Route Handlers introduces an extra HTTP round trip, which is less efficient than fetching data directly from the source. For client-side search, this is not a concern, as the client needs to make a request anyway.
When to Use Server Actions
-
Mutations, Not Queries: Server Actions are primarily designed for mutating data (e.g., creating, updating, or deleting records), not for data fetching or queries. Using Server Actions for data fetching is not recommended, as they are queued and executed sequentially, which can introduce performance bottlenecks for search or other parallelizable queries How to use Next.js as a backend for your frontend.
-
Type Safety and Simplicity: Some developers use Server Actions for queries due to their RPC-like nature and type safety, but this is not the intended use case and can lead to slower, sequential execution Github Issue: Parallel server action calls.
Downsides
- Route Handlers: Slightly more boilerplate, but they are the standard way to expose public endpoints for data fetching.
- Server Actions: Not recommended for queries due to sequential execution and potential performance issues. Best reserved for mutations.
Summary
- For searching a database (queries): Use a Route Handler (API endpoint).
- For mutating data: Use Server Actions.
This approach aligns with Next.js best practices and ensures optimal performance and maintainability for your application How to use Next.js as a backend for your frontend.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost what about fetching data in an on demand server component, or are routers handlers still superior
Let me check the knowledge base for you again. Give me one moment! :books:
If you are fetching data in an on-demand Server Component (i.e., a Server Component rendered at request time), the recommended approach in Next.js is to fetch data directly from its source (such as your database or an external API), not via Route Handlers. This is because:
- Direct fetching is more efficient: Fetching data directly in Server Components avoids the extra HTTP round trip that occurs when you fetch from a Route Handler. This results in better performance and a simpler architecture.
- Build-time limitations: For Server Components that are pre-rendered at build time, using Route Handlers will fail the build step, since there is no server listening for these requests during the build process.
- On-demand rendering: For Server Components rendered on demand, fetching from Route Handlers is slower due to the additional HTTP request between the handler and the render process.
In summary:
For on-demand Server Components, fetching data directly from the source is preferred and considered superior to using Route Handlers for data fetching. Route Handlers are best used when you need to expose an API endpoint for client-side consumption or for integrating with external services How to use Next.js as a backend for your frontend.
Fetch data in Server Components directly from its source, not via Route Handlers. For Server Components pre-rendered at build time, using Route Handlers will fail the build step. For Server Components rendered on demand, fetching from Route Handlers is slower due to the extra HTTP round trip between the handler and the render process.
— How to use Next.js as a backend for your frontend
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost I want you to compare using an on demand server component to search names in a db vs exposing a route handler and doing it from the client
Let me check the knowledge base for you again. Give me one moment! :books:
Certainly! Here’s a direct comparison of the two approaches for searching names in a database in Next.js, based strictly on the official documentation:
1. On-demand Server Component
How it works:
You fetch data directly from your database (or data source) inside a Server Component that is rendered on demand (i.e., at request time, not at build time).
Pros:
- Performance: Fetching directly from the source is more efficient—there’s no extra HTTP round trip, as the data is fetched server-side and rendered directly into the page.
- Security: Secrets (like database credentials) remain on the server and are never exposed to the client.
- Simplicity: No need to maintain a separate API layer for internal data needs.
Cons:
- Interactivity: If you want real-time search (e.g., as the user types), you’ll need to reload the page or use client-side navigation to trigger new server renders, which may not feel as smooth as client-side fetching.
- Not suitable for client-only APIs: If your search depends on browser APIs or needs to be called from the client (e.g., for instant search), this approach is less flexible.
Reference:
Fetch data in Server Components directly from its source, not via Route Handlers. For Server Components rendered on demand, fetching from Route Handlers is slower due to the extra HTTP round trip between the handler and the render process.
How to use Next.js as a backend for your frontend
2. Exposing a Route Handler and Fetching from the Client
How it works:
You create a Route Handler (API endpoint) that performs the search, and your client-side code (e.g., using fetch in a Client Component or with SWR) calls this endpoint as the user interacts with the search field.
Pros:
- Interactivity: Enables real-time, interactive search experiences (e.g., instant results as the user types) without full page reloads.
- Decoupling: The API can be reused by other clients or services.
- Flexibility: Works well for data that depends on client-only APIs or needs to be polled frequently.
Cons:
- Performance: Adds an extra HTTP round trip (client → server → database), which can be less efficient than direct server-side fetching.
- Complexity: Requires maintaining an API layer, and you must ensure proper security and validation on the endpoint.
Reference:
For these [client-side] cases, use community libraries like swr or react-query for client fetching.
How to use Next.js as a backend for your frontend
Summary Table
| Approach | Performance | Interactivity | Use Case |
|---|---|---|---|
| On-demand Server Component | Best | Limited | SSR/SSG, secure, not real-time search |
| Route Handler + Client Fetching | Good | Best | Real-time/instant search, client-driven needs |
In conclusion:
- Use an on-demand Server Component if you want the best performance and security, and your search does not need to be interactive or real-time.
- Use a Route Handler with client-side fetching if you need a highly interactive search experience (e.g., instant results as the user types).
If you need both SSR and interactivity, you can combine both: render initial results server-side, then enhance with client-side fetching for subsequent searches.
If you need more details or code examples, let me know!
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost how about for fetching data for a chart, but i dont want the data to be accessible from an api route. can i lazily call a server action?
Let me check the knowledge base for you again. Give me one moment! :books:
If you want to fetch data for a chart and do not want the data to be accessible from an API route, you can use a Server Action. Server Actions allow you to run server-side code from the client without exposing a public API endpoint in the same way as a Route Handler. This can help keep your data access logic internal and not directly accessible via a REST endpoint.
However, Server Actions are primarily designed for mutations (i.e., updating data), not for data fetching. Using them for fetching data is possible, but there are some caveats:
- Sequential Execution: Server Actions are queued and executed sequentially, which can introduce performance bottlenecks if you need to fetch data in parallel or for highly interactive use cases like charts that update frequently How to use Next.js as a backend for your frontend.
- Not Recommended for Data Fetching: The documentation and community discussions explicitly state that Server Actions are not recommended for data fetching, and frameworks typically process one action at a time without caching the return value Github Issue: Parallel server action calls.
Server Actions let you run server-side code from the client. Their primary purpose is to mutate data from your frontend client. Server Actions are queued. Using them for data fetching introduces sequential execution.
— How to use Next.js as a backend for your frontend
Lazily calling a Server Action:
You can invoke a Server Action from a Client Component (e.g., in response to a button click or other event), which is a form of "lazy" invocation. This does not expose a public API route, but the action is still accessible to the client code that imports and calls it Using Server Actions in Client Components.
Summary:
- You can lazily call a Server Action from the client to fetch data for a chart, and this does not expose a public API route.
- However, this approach is not recommended for data fetching due to sequential execution and lack of caching.
- If you need high performance or parallel data fetching, consider other approaches, but be aware that using Server Actions for queries is not the intended use case in Next.js.
If you have strict requirements to avoid public API routes and are okay with the limitations, Server Actions can be used for this purpose, but be mindful of the trade-offs.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost the docs mention fetching data in a server component is ideal, but my chart library needs to be in the dom. are route handlers my only approach?
Let me check the knowledge base for you again. Give me one moment! :books:
If your chart library requires access to the DOM (i.e., it must run in a Client Component), you cannot render it directly in a Server Component, since Server Components do not have access to the DOM or browser APIs (Server and Client Components).
The recommended pattern in this scenario is:
- Fetch data in a Server Component (ideally, for security and performance).
- Pass the fetched data as props to your Client Component (the chart), which then renders the chart using the data.
This way, your data fetching remains secure and efficient, and your chart library can access the DOM as needed. Here’s a simplified example:
// Server Component
import Chart from './Chart'
export default async function Page() {
const data = await getChartData() // fetch from DB or API
return <Chart data={data} />
}
// Client Component (Chart.tsx)
'use client'
export default function Chart({ data }) {
// Render chart using data and DOM APIs
}
Route Handlers are only necessary if you want to fetch data from the client at runtime (e.g., for dynamic or interactive updates), or if you need to expose an API endpoint for client-side fetching (such as with SWR or React Query). If you can fetch all necessary data server-side and pass it down, you do not need a Route Handler (Server and Client Components).
In summary:
- You do not have to use Route Handlers if you can fetch data in a Server Component and pass it to your Client Component.
- Use Route Handlers only if you need client-initiated, on-demand fetching (e.g., for live updates or user-driven queries).
If you need more interactivity (e.g., live updates), then Route Handlers or client-side fetching libraries become necessary. Otherwise, the Server Component → Client Component pattern is ideal and recommended.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost how to best structure that approach? can it be written into the same file or must it be separate to support the use client directive
Let me check the knowledge base for you again. Give me one moment! :books:
To use the 'use client' directive, you must place it at the top of a file, and it applies to the entire file. This means that if you want to have both a Server Component (for data fetching) and a Client Component (for rendering the chart with DOM access), they need to be in separate files.
- The Server Component (e.g.,
page.tsx) fetches the data and passes it as props. - The Client Component (e.g.,
Chart.tsx) is in a separate file with the'use client'directive at the top.
You cannot mix Server and Client Components in the same file. The 'use client' directive marks the file and all its exports as Client Components, so any server-only logic (like direct data fetching or using secrets) should remain in a Server Component file.
Example structure:
// app/page.tsx (Server Component)
import Chart from './Chart'
export default async function Page() {
const data = await getChartData()
return <Chart data={data} />
}
// app/Chart.tsx (Client Component)
'use client'
export default function Chart({ data }) {
// Chart rendering logic that needs the DOM
}
This structure is required and recommended by the Next.js documentation for proper separation of server and client logic Server and Client Components use client directive.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost will this fetch block the render of the whole page or just the component? do i need suspense/dynamic
Let me check the knowledge base for you again. Give me one moment! :books:
If you fetch data in the Server Component (e.g., in page.tsx) without wrapping the Client Component (e.g., your Chart) in a <Suspense> boundary, the data fetching will block the render of the whole page until the data is available. This is because the Server Component must resolve all its async data before rendering its children, including any Client Components it passes data to (Sequential data fetching).
If you want to stream the page and show a loading state for just the chart (or any other async section), you should wrap the Client Component in a <Suspense> boundary and pass a fallback. This allows the rest of the page to render immediately, while the chart component loads and renders once its data is ready (Streaming with Suspense).
Example:
import { Suspense } from 'react'
import Chart from './Chart'
export default function Page() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<div>Loading chart...</div>}>
<ChartWrapper />
</Suspense>
</div>
)
}
// ChartWrapper is a Server Component that fetches data and renders <Chart>
async function ChartWrapper() {
const data = await getChartData()
return <Chart data={data} />
}
- Without Suspense: The whole page waits for
getChartData()to resolve. - With Suspense: The page renders immediately, and only the chart section waits for the data.
You do not need to use next/dynamic for this pattern unless you want to lazy-load the Client Component's code itself. <Suspense> is sufficient for streaming and loading states (Streaming with Suspense).
Summary:
- By default, data fetching in a Server Component blocks the whole page render.
- Use
<Suspense>to stream the page and show a loading state for just the chart or any async section. next/dynamicis only needed for code-splitting/lazy-loading Client Components, not for streaming data.
Streaming with Suspense
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost im concerned that since i have many charts, ill need to now create loads of server component wrappers. is there a way to simplify or consolidate them
Let me check the knowledge base for you again. Give me one moment! :books:
Your concern is valid: if you have many charts, creating a separate Server Component wrapper for each one can feel repetitive and add boilerplate. The official Next.js documentation does not provide a built-in abstraction for this pattern, but it does show that you can generalize the approach by creating a generic Server Component wrapper that can be reused for multiple charts.
For example, you could create a single Server Component that takes a data-fetching function and a Client Component as props, then fetches the data and renders the Client Component with that data. However, the documentation does not provide a concrete example of this pattern, and you would need to ensure type safety and proper usage.
The key point from the docs is that Server and Client Components must be in separate files, and the Server Component is responsible for fetching data and passing it to the Client Component (Server and Client Components). There is no mention of a built-in way to avoid creating these wrappers if you want to keep data fetching on the server and DOM access in the client.
In summary:
- The knowledge sources do not mention a built-in way to avoid creating multiple Server Component wrappers for many charts.
- You can try to abstract the pattern yourself, but you must keep the separation between Server and Client Components as required by Next.js.
If you need to reduce boilerplate, consider creating a generic wrapper component, but this is not explicitly documented in the official sources.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@oblique pollen Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@dry frost so is this wrapper ive written a common pattern or is there a more standard way
export async function FetchAndSuspend <T>({
children_fn,
fetch_fn,
fallback = null,
}: FetchAndSuspendProps<T>) {
try {
const data = await fetch_fn();
return children_fn(data);
} catch (error) {
console.error("Error fetching data:", error);
return fallback;
}
}
Let me check the knowledge base for you again. Give me one moment! :books:
Based on the official Next.js documentation, the pattern you’ve written—a generic server component wrapper that fetches data and renders children—is not explicitly documented as a standard or recommended approach. The docs consistently show examples where each Server Component fetches its own data and passes it to a Client Component, but they do not provide or mention a generic abstraction like your FetchAndSuspend component Server and Client Components.
The official pattern is to create a dedicated Server Component for each data-fetching use case, which then passes the data to a Client Component. While your abstraction could help reduce boilerplate, it is not shown or discussed in the knowledge sources, so it cannot be considered a documented or "standard" Next.js approach.
In summary:
- Your wrapper is not shown in the official docs and is not a documented standard pattern.
- The standard documented approach is to create a Server Component per data-fetching use case and pass the data to a Client Component in a separate file Server and Client Components.