What is the best way to design a basic server component that has the ability to retrieve data in NextJS 13?

Exploring the world of NextJS, I am currently utilizing NextJS 13 with the new app directory instead of the traditional pages directory structure. Despite trying various methods to fetch data, none seem to be working as expected. The process should be straightforward:

In src/app/page.tsx:

export interface ToDo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

const getToDos = async (): Promise<ToDo[]> => {
  const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
  console.log(`Status: ${res.status}`); // prints 'Status: 200'
  return res.json();
}

const Home = async () => {
  const toDos = await getToDos();
  return (
    <ul>
      {toDos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

export default Home;

The JSON response from the endpoint is:

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

Upon execution, both a successful 200 status and an error message are displayed:

Warning: Only plain objects can be passed to Client Components from Server Components. Classes or other objects with methods are not supported.
  <... client={{queryCache: ..., mutationCache: ..., logger: ..., defaultOptions: ..., queryDefaults: ..., mutationDefaults: ..., mountCount: ...}} children=...>
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. Classes or other objects with methods are not supported.
  {queryCache: {listeners: Set, subscribe: function, config: ..., queries: ..., queriesMap: ...}, mutationCache: ..., logger: ..., defaultOptions: ..., queryDefaults: ..., mutationDefaults: ..., mountCount: ...}
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Status: 200

-  ┌ GET / 200 in 606ms
   │
   └──── GET https://jsonplaceholder.typicode.com/todos/1 200 in 67ms (cache: HIT)

- error node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js (1863:12) @ resolveModelToJSON
- error Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
  {listeners: Set, subscribe: function, config: ..., queries: ..., queriesMap: ...}
                              ^^^^^^^^
    at stringify (<anonymous>)
null
- error node_modules/next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.edge.development.js (1863:12) @ resolveModelToJSON
- error Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
  {listeners: Set, subscribe: function, config: ..., queries: ..., queriesMap: ...}
                              ^^^^^^^^
    at stringify (<anonymous>)
digest: "501055159"
null

Introducing "use server" at the start of page.tsx triggers compilation errors:

Server Actions require `experimental.serverActions` option to be enabled in your Next.js config: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions

While my goal is to generate HTML statically, for now, I would greatly appreciate functional data fetching scripts.

Update

Having previously experimented with Tanstack Query, I have since removed all related code from my layout.tsx file. However, I now encounter a different issue with the following code:

export default async function Home(): Promise<JSX.Element> {
  const todos = await getToDos();
  console.log(todos);
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

resulting in the error:

error TypeError: todos.map is not a function

Answer №1

It appears that you are attempting to transfer a function from a server component to a client component, which is not allowed unless it is a server action. Currently in alpha stage, server actions need to be enabled by configuring experimental.serverActions in your next.config.js file:

module.exports = {
  experimental: {
    serverActions: true,
  },
};

Below is an example of a server-side component:

import "server-only";

interface ToDo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

async function getToDos(): Promise<ToDo[]> {
  const res = await fetch("https://jsonplaceholder.typicode.com/todos");
  return await res.json(); // You missed an 'await' here
}

export default async function Home(): Promise<JSX.Element> {
  const todos = await getToDos();
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

I highly recommend installing the server-only package to prevent errors when trying to import a server component into the client side.

Answer №2

As @FabioNettis astutely observed, I was attempting to retrieve a lone object that was not iterable. By mistakenly trying to fetch from the incorrect path /todos/1 instead of /todos, my fetching code appeared to be accurate, albeit subpar. Implementing proper error handling would have facilitated the debugging process.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Strategies for effectively searching and filtering nested arrays

I'm facing a challenge with filtering an array of objects based on a nested property and a search term. Here is a sample array: let items = [ { category: 15, label: "Components", value: "a614741f-7d4b-4b33-91b7-89a0ef96a0 ...

Error: The function "navigate" has not been declared or defined

Just starting out in reactjs and embarking on a new project. I've successfully implemented a register and login function using firebase, but hit a snag when trying to navigate to a new page like the dashboard upon user login, I encountered this error: ...

Extract HTML content using CKEditor

Hey there! I'm in need of some help with getting user-entered data from a textarea. I've already attempted using CKEDITOR.instances.editor1.getData() and CKEDITOR.instances.ckeditor.document.getBody.getHtml(), but unfortunately both only return ...

Combining Vitest with FastifyAutoload resulted in a FastifyError: The plugin provided must either be a function or a promise, but instead, an 'object' was received

My application built on Fastify ("fastify": "^4.26.0") operates smoothly under normal conditions with no issues. However, when trying to incorporate unit testing using Vitest, every test fails despite their simplicity. Upon troubleshoot ...

What is the best approach for presenting multiple entries with identical IDs in a table using Angular?

I obtained a JSON response in the following structure; https://jsonplaceholder.typicode.com/posts My goal is to format the table to resemble the attached image structure. However, my current implementation is displaying repeating user IDs. How can I adju ...

incorporating my unique typographic styles into the MUI framework

I'm currently working on customizing the typography for my TypeScript Next.js project. Unfortunately, I am facing difficulties in configuring my code properly, which is causing it to not work as expected. Can someone kindly provide assistance or guida ...

Oops! The API request was denied with error code 401 - Unauthorized in React

I have been working on incorporating an API into my front-end project using React/Typescript. The documentation for the API specifies that authorization requires a key named token with a corresponding value, which should be included in the header. To stor ...

Is it possible to derive a TypeScript interface from a Mongoose schema without including the 'Document' type?

Using ts-mongoose allows me to define interfaces and schemas for my data in one place. I then export them as a mongoose schema along with the actual interface. The challenge I'm facing is finding a simple way to extract that interface without includi ...

Utilize IDE's capabilities to recommend mutations and actions during the process of committing or dispatching

In my current Vue 3 Typescript project, I am utilizing Vuex. The code snippet below showcases how I have implemented it: import { createStore, useStore as baseUseStore, Store } from 'vuex'; import { InjectionKey } from 'vue'; export i ...

Adding a new value to an array of objects without altering the existing values in ReactJS and NextJS

I have a JSON file containing image names that I need to organize into a Key-Value Object. I am currently using regex to create keys by removing "-img[image No]". However, I am having trouble storing all the image names in the array without overwriting pre ...

Chakra UI Solid Button loses its background color

I recently integrated the button component from Chakra UI into my Next.js project. However, I encountered an issue where the background color of the button variant "solid" would disappear and only show up when hovered over. I experimented with other varian ...

String date in the format "dd-MM-yyyy" cannot be transformed into a date using the 'DatePipe' function

Having trouble with date conversion in my custom pipe. It seems that when using a locale of 'nl-nl' and trying to format the date as 'dd-MM-YYYY', I'm seeing an error message stating Unable to convert "16-02-2023" into a ...

The error message indicates that the argument cannot be assigned to the parameter type 'AxiosRequestConfig'

I am working on a React app using Typescript, where I fetch a list of items from MongoDB. I want to implement the functionality to delete items from this list. The list is displayed in the app and each item has a corresponding delete button. However, when ...

Using React to iterate through the child components of the parent

I have created a component that can accept either a single child or multiple children. Here is an example with multiple children: <SideDataGridItem> <div id='top'> <div>A1</div> <div>B1</div> ...

Struggling with NextAuth and Middleware - unable to leave signin page post logging in. What could be causing the absence of a token?

After implementing the middleware.ts in my project, I encountered an issue where the user would not be redirected to the callbackUrl and instead remained on the signing page. Following the documentation, I initially started with the following code in my m ...

Is bundling a Node.js backend a wise decision or a mistake?

Just a thought that crossed my mind - I understand the advantages of bundling client-side code, but what about bundling server-side code with Browserify/Webpack? Is this considered a best practice? ...

Are there any methods for utilizing the Angular/flex-layout API within a TypeScript file in an Angular 11 project?

When working with Angular Material, the Angular Flex Layout proves to be quite beneficial. Is there a way to access the flex layout API within a TypeScript file? For instance, can we retrieve MediaQueries values from this link in a TypeScript file? breakp ...

Issue encountered with passport-local in TypeScript: Unable to utilize 'new' with an expression that does not have a call or construct signature

Currently, I am attempting to implement the passport-local package in TypeScript (version 2.0.0RC); however, a compiler error has arisen: Error TS2351: It is not possible to use 'new' with an expression lacking a call or construct signature. ...

Exploring Nextjs with server-side rendering and fetching data from

When utilizing the getServerSideProps function in Next.js to make a fetch request to my API, I encountered an issue where the origin header was coming back as undefined. This seems to be specific to requests made from the server in Next.js, as I am able ...

position the tooltip within the ample available space of a container in an angular environment

In my editor, users can create a banner and freely drag elements within it. Each element has a tooltip that should appear on hover, positioned on the side of the element with the most space (top, left, bottom, right). The tooltip should never extend outsid ...