Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How do I create a simple server component that can fetch data in NextJS 13?

I am learning NextJS. Currently, I am using NextJS 13 with the new app directory instead of the old pages directory. I have tried various ways of fetching data, but I have not gotten any of them to work. It should be as simple as

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 returned by the endpoint is

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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

This is the output. Notice how I am getting both a 200 OK status and an error message:

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

If I add "use server" to the beginning of page.tsx, I get a compilation error:

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

Ideally I would like to statically generate HTML, but for now I would be grateful for any working fetching code.

Update

One of the previous solutions I tried was to use Tanstack Query. I have now removed all of that code from my layout.tsx. Now I get a different error for this 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>
  );
}

saying

error TypeError: todos.map is not a function

>Solution :

It seems you are trying to pass a function from a server component to a client component, this is not possible unless it is a server action, since server actions are still a feature currently in alpha, you need to enable them by setting experimental.serverActions in your next.config.js file:

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

Here 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(); // by the way, you were missing 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 strongly recommend you install the server-only package, so an error is thrown if you try to import a server component into the client side.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading