Next.js: Documentation needs to mention writing server-side code in GS(S)P props earlier

Created on 11 Aug 2020  ·  6Comments  ·  Source: vercel/next.js

Lots of users miss that they can write server side code in getStaticProps and getServerSideProps.

We also see numerous issues about people trying to fetch API routes. We need to fix this in the documentation by making this one of the first things we have in the docs.

p1 documentation

Most helpful comment

Throwing my opinion down here - I don't think this is fixed. I found this issue via blame'ing the note in the docs because it confused me even further. Why can we not fetch API routes? This has broken my mental model of how Next.js used to work (i.e. fetch from API during SSR).

Two notes on the content of the fix:

Instead, directly import the API route and call its function yourself.

How do we call the function itself? How do we access the data? Do you mean repeat the data fetching (i.e. DB calls) in the API route in the getServerSideProps method? I think I'm misunderstanding something here.

You may need to slightly refactor your code for this approach.

Without any indication how to approach this refactor or what the refactor entails makes for a pretty frustrating read. I think pointing to an example here of an app using API routes in SSR would be really useful. I'm currently lost and regretting my decision to use API routes because of the complexity and hidden constraints.

All 6 comments

I will take this up.

Throwing my opinion down here - I don't think this is fixed. I found this issue via blame'ing the note in the docs because it confused me even further. Why can we not fetch API routes? This has broken my mental model of how Next.js used to work (i.e. fetch from API during SSR).

Two notes on the content of the fix:

Instead, directly import the API route and call its function yourself.

How do we call the function itself? How do we access the data? Do you mean repeat the data fetching (i.e. DB calls) in the API route in the getServerSideProps method? I think I'm misunderstanding something here.

You may need to slightly refactor your code for this approach.

Without any indication how to approach this refactor or what the refactor entails makes for a pretty frustrating read. I think pointing to an example here of an app using API routes in SSR would be really useful. I'm currently lost and regretting my decision to use API routes because of the complexity and hidden constraints.

@adriancooney Exactly! It's extra confusing when at least a few of the examples use fetch in exactly the way that the docs say not to? 🤷🏻‍♂️

Throwing my opinion down here - I don't think this is fixed. I found this issue via blame'ing the note in the docs because it confused me even further. Why can we not fetch API routes? This has broken my mental model of how Next.js used to work (i.e. fetch from API during SSR).

Two notes on the content of the fix:

Instead, directly import the API route and call its function yourself.

How do we call the function itself? How do we access the data? Do you mean repeat the data fetching (i.e. DB calls) in the API route in the getServerSideProps method? I think I'm misunderstanding something here.

You may need to slightly refactor your code for this approach.

Without any indication how to approach this refactor or what the refactor entails makes for a pretty frustrating read. I think pointing to an example here of an app using API routes in SSR would be really useful. I'm currently lost and regretting my decision to use API routes because of the complexity and hidden constraints.

I just went through the exact same procedure, using the 'blame' feature. While #16198 added new docs, there are no examples about calling API routes from getServerSideProps without closing the page response's stream through the API handler. How should we obtain data from API routes when called directly through getServerSideProps or getStaticProps?

I just came across this comment with a potential solution. So, after all, an API route may have a named export which could be called by getServerSideProps or getStaticProps, like below:

diff --git a/pages/api/test.ts b/pages/api/test.ts
index 7ecb211..28d9e7e 100644
--- a/pages/api/test.ts
+++ b/pages/api/test.ts
@@ -1,15 +1,17 @@
 import Cors from "micro-cors";
 const cors = Cors({ allowMethods: ["GET", "HEAD"] });

+export function getData() {
+    return {
+        name: "Home",
+        date: new Date().toLocaleString()
+    };
+}
+
 function test(req, res) {
     res.statusCode = 200;
     res.setHeader("Content-Type", "application/json");
-    res.end(
-        JSON.stringify({
-            name: "Home",
-            date: new Date().toLocaleString()
-        })
-    );
+    res.end(JSON.stringify(getData()));
 }

 export default cors(test);
diff --git a/pages/ssr/[slug].tsx b/pages/ssr/[slug].tsx
index d2484e2..ece5db9 100644
--- a/pages/ssr/[slug].tsx
+++ b/pages/ssr/[slug].tsx
@@ -1,5 +1,6 @@
 import "isomorphic-unfetch";
 import { Regenerate } from "../../src/components/Regenerate";
+import { getData } from "../api/test";

 function Index(props: any) {
     return (
@@ -35,9 +36,7 @@ function Index(props: any) {
 }

 export async function getServerSideProps(context: any) {
-    const res = await fetch(
-        "https://static-next.willemliu.now.sh/api/test"
-    ).then((res) => res.json());
+    const res = await getData();
     console.log("getServerProps", res, context.params, context.query);
     return {
         props: { ...res, name: context.query.slug }

I figured out how to do this with the help of next-connect. Not sure if there are better ways, but this worked for me:

In the page calling getServerSideProps, ie. /pages/games.js:

import middleware from "/path/to/middleware.js"
...
export const getServerSideProps = async ({ req, res }) => {
  await middleware.apply(req, res);
  const games = await Game.find().lean();
  return {
    props: {
      initialData: JSON.parse(JSON.stringify({ games })) || null, // https://github.com/vercel/next.js/discussions/11209#discussioncomment-35915
    },
  };
};

middleware.js:

import nextConnect from "next-connect";
import db from "path/to/db";

const middleware = nextConnect();

middleware.use(db);

export default middleware;

db.js:

import mongoose from "mongoose";
import dbConnect from "path/to/dbConnect";

const db = async (req, res, next) => {
  // https://mongoosejs.com/docs/api.html#connection_Connection-readyState
  if (mongoose.connections[0].readyState !== 1) {
    await dbConnect();
  }
  return next();
};
export default db;

dbConnect.js:

import mongoose from "mongoose";

const dbConnect = async () => {
  // https://mongoosejs.com/docs/api.html#connection_Connection-readyState
  if (mongoose.connections[0].readyState) return;

  await mongoose.connect(process.env.MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useFindAndModify: false,
    useCreateIndex: true,
  });
};

export default dbConnect;

Here's my repository where this came from, if you wanna see the whole thing.

Good luck, y'all!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nickredmark picture nickredmark  ·  60Comments

timneutkens picture timneutkens  ·  250Comments

tomaswitek picture tomaswitek  ·  73Comments

timneutkens picture timneutkens  ·  72Comments

matthewmueller picture matthewmueller  ·  102Comments