简体   繁体   English

使用 getServerSideProps 获取内部 API? (下一个.js)

[英]Internal API fetch with getServerSideProps? (Next.js)

I'm new to Next.js and I'm trying to understand the suggested structure and dealing with data between pages or components.我是 Next.js 的新手,我正在尝试了解建议的结构并处理页面或组件之间的数据。

For instance, inside my page home.js , I fetch an internal API called /api/user.js which returns some user data from MongoDB.例如,在我的页面home.js中,我获取了一个名为/api/user.js的内部 API,它从 MongoDB 返回一些用户数据。 I am doing this by using fetch() to call the API route from within getServerSideProps() , which passes various props to the page after some calculations.我通过使用fetch()getServerSideProps()中调用 API 路由来做到这一点,该路由在一些计算后将各种道具传递到页面。

From my understanding, this is good for SEO, since props get fetched/modified server-side and the page gets them ready to render.据我了解,这对 SEO 有好处,因为道具在服务器端被获取/修改,并且页面让它们准备好呈现。 But then I read in the Next.js documentation that you should not use fetch() to all an API route in getServerSideProps() .但是后来我在 Next.js 文档中读到,您不应该对getServerSideProps()中的所有 API 路由使用fetch() ) 。 So what am I suppose to do to comply to good practice and good SEO?那么我应该怎么做才能遵守良好的做法和良好的 SEO?

The reason I'm not doing the required calculations for home.js in the API route itself is that I need more generic data from this API route, as I will use it in other pages as well.我没有在 API 路由本身中对home.js进行所需计算的原因是我需要来自此 API 路由的更多通用数据,因为我也会在其他页面中使用它。

I also have to consider caching, which client-side is very straightforward using SWR to fetch an internal API, but server-side I'm not yet sure how to achieve it.我还必须考虑缓存,使用 SWR 获取内部 API 的客户端非常简单,但服务器端我还不确定如何实现它。

home.js : home.js

export default function Page({ prop1, prop2, prop3 }) {
        // render etc.
}

export async function getServerSideProps(context) {
  const session = await getSession(context)
  let data = null
  var aArray = [], bArray = [], cArray = []
  const { db } = await connectToDatabase()

  function shuffle(array) {
    var currentIndex = array.length, temporaryValue, randomIndex;
    while (0 !== currentIndex) {
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex -= 1;
      temporaryValue = array[currentIndex];
      array[currentIndex] = array[randomIndex];
      array[randomIndex] = temporaryValue;
    }
    return array;
  }

  if (session) {
    const hostname = process.env.NEXT_PUBLIC_SITE_URL
    const options = { headers: { cookie: context.req.headers.cookie } }
    const res = await fetch(`${hostname}/api/user`, options)
    const json = await res.json()
    if (json.data) { data = json.data }

    // do some math with data ...
    // connect to MongoDB and do some comparisons, etc.

But then I read in the Next.js documentation that you should not use fetch() to all an API route in getServerSideProps() .但是后来我在 Next.js 文档中读到,您不应该对getServerSideProps()中的所有 API 路由使用fetch() ) 。

You want to use the logic that's in your API route directly in getServerSideProps , rather than calling your internal API.您想直接在getServerSideProps中使用 API 路由中的逻辑,而不是调用您的内部 API。 That's because getServerSideProps runs on the server just like the API routes (making a request from the server to the server itself would be pointless).这是因为getServerSideProps就像 API 路由一样在服务器上运行(从服务器向服务器本身发出请求将毫无意义)。 You can read from the filesystem or access a database directly from getServerSideProps .您可以从文件系统读取或直接从getServerSideProps访问数据库。 Note that this only applies to calls to internal API routes - it's perfectly fine to call external APIs from getServerSideProps .请注意,这仅适用于对内部 API 路由的调用——从getServerSideProps调用外部 API 非常好。

From Next.js getServerSideProps documentation:来自 Next.js getServerSideProps文档:

It can be tempting to reach for an API Route when you want to fetch data from the server, then call that API route from getServerSideProps .当您想从服务器获取数据时,可能很容易使用 API 路由,然后从getServerSideProps调用该 API 路由。 This is an unnecessary and inefficient approach, as it will cause an extra request to be made due to both getServerSideProps and API Routes running on the server.这是一种不必要且低效的方法,因为它会由于服务器上运行的getServerSideProps和 API 路由而导致发出额外的请求。

(...) Instead, directly import the logic used inside your API Route into getServerSideProps . (...) 相反,直接将 API 路由中使用的逻辑导入getServerSideProps This could mean calling a CMS, database, or other API directly from inside getServerSideProps .这可能意味着直接从getServerSideProps内部调用 CMS、数据库或其他 API。

(Note that the same applies when using getStaticProps / getStaticPaths methods ) (请注意,使用getStaticProps / getStaticPaths方法时同样适用


Here's a small refactor example that allows you to have logic from an API route reused in getServerSideProps .这是一个小的重构示例,它允许您在getServerSideProps中重用来自 API 路由的逻辑。

Let's assume you have this simple API route.假设您有这个简单的 API 路由。

// pages/api/user
export default async function handler(req, res) {
    // Using a fetch here but could be any async operation to an external source
    const response = await fetch(/* external API endpoint */)
    const jsonData = await response.json()
    res.status(200).json(jsonData)
}

You can extract the fetching logic to a separate function (can still keep it in api/user if you want), which is still usable in the API route.您可以将获取逻辑提取到单独的 function (如果需要,仍可以将其保留在api/user中),它仍然可以在 API 路由中使用。

// pages/api/user
export async function getData() {
    const response = await fetch(/* external API endpoint */)
    const jsonData = await response.json()
    return jsonData
}

export default async function handler(req, res) {
    const jsonData = await getData()
    res.status(200).json(jsonData)
}

But also allows you to re-use the getData function in getServerSideProps .但也允许您在getServerSideProps中重新使用getData function 。

// pages/home
import { getData } from './api/user'

//...

export async function getServerSideProps(context) {
    const jsonData = await getData()
    //...
}

You want to use the logic that's in your API route directly in getServerSideProps, rather than calling your internal API.您想直接在 getServerSideProps 中使用 API 路由中的逻辑,而不是调用内部 API。 That's because getServerSideProps runs on the server just like the API routes (making a request from the server to the server itself would be pointless).这是因为 getServerSideProps 就像 API 路由一样在服务器上运行(从服务器向服务器本身发出请求将毫无意义)。 You can read from the filesystem or access a database directly from getServerSideProps您可以从文件系统读取或直接从 getServerSideProps 访问数据库

As I admit, what you say is correct but problem still exist.我承认,你说的是对的,但问题仍然存在。 Assume you have your backend written and your api's are secured so fetching out logic from a secured and written backend seems to be annoying and wasting time and energy.假设您已经编写了后端并且您的 api 是安全的,因此从安全和编写的后端提取逻辑似乎很烦人并且浪费时间和精力。 Another disadvantage is that by fetching out logic from backend you must rewrite your own code to handle errors and authenticate user's and validate user request's that exist in your written backend.另一个缺点是,通过从后端提取逻辑,您必须重写自己的代码来处理错误并验证用户并验证您编写的后端中存在的用户请求。 I wonder if it's possible to call api's within nextjs without fetching out logic from middlewars?我想知道是否可以在 nextjs 中调用 api 而无需从中间战中获取逻辑? The answer is positive here is my solution: npm i node-mocks-http答案是肯定的,这是我的解决方案: npm i node-mocks-http

import httpMocks from "node-mocks-http";
import newsController from "./api/news/newsController";
import logger from "../middlewares/logger";
import dbConnectMid from "../middlewares/dbconnect";
import NewsCard from "../components/newsCard";
export default function Home({ news }) {
  return (
    <section>
      <h2>Latest News</h2>
      <NewsCard news={news} />
    </section>
  );
}
export async function getServerSideProps() {
  let req = httpMocks.createRequest();
  let res = httpMocks.createResponse();
  async function callMids(req, res, index, ...mids) {
    index = index || 0;
    if (index <= mids.length - 1)
      await mids[index](req, res, () => callMids(req, res, ++index, ...mids));
  }
  await callMids(
    req,
    res,
    null,
    dbConnectMid,
    logger,
    newsController.sendAllNews
  );
  return {
    props: { news: res._getJSONData() },
  };
}

important NOTE: don't forget to use await next() instead of next() if you use my code in all of your middlewares or else you get an error.重要注意事项:如果您在所有中间件中使用我的代码,请不要忘记使用await next()而不是next() ,否则会出错。 Another solution: next connect has run method that do something like mycode but personally I had some problems with it;另一个解决方案: next connect 有run类似 mycode 的方法,但我个人遇到了一些问题; here is its link: next connet run method to call next api's in serverSideProps这是它的链接: next connet run method to call next api's in serverSideProps

Just try to use useSWR , example below只需尝试使用useSWR ,示例如下

import useSWR from 'swr'
import React from 'react';

//important to return only result, not Promise
const fetcher = (url) => fetch(url).then((res) => res.json());

const Categories = () => {
 //getting data and error
 const { data, error } = useSWR('/api/category/getCategories', fetcher)
 if (error) return <div>Failed to load</div>
 if (!data) return <div>Loading...</div>
 if (data){
   // {data} is completed, it's ok!
   //your code here to make something with {data}
   return (
      <div>
      //something here, example {data.name}
      </div>
   )
 }
}

export default Categories

Please notice, fetch only supports absolute URLs , it's why I don't like to use it.请注意, fetch支持绝对 URL ,这就是我不喜欢使用它的原因。

PS According to the docs , you can even use useSWR with SSR . PS 根据文档,您甚至可以将useSWRSSR一起使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM