繁体   English   中英

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

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

我是 Next.js 的新手,我正在尝试了解建议的结构并处理页面或组件之间的数据。

例如,在我的页面home.js中,我获取了一个名为/api/user.js的内部 API,它从 MongoDB 返回一些用户数据。 我通过使用fetch()getServerSideProps()中调用 API 路由来做到这一点,该路由在一些计算后将各种道具传递到页面。

据我了解,这对 SEO 有好处,因为道具在服务器端被获取/修改,并且页面让它们准备好呈现。 但是后来我在 Next.js 文档中读到,您不应该对getServerSideProps()中的所有 API 路由使用fetch() ) 。 那么我应该怎么做才能遵守良好的做法和良好的 SEO?

我没有在 API 路由本身中对home.js进行所需计算的原因是我需要来自此 API 路由的更多通用数据,因为我也会在其他页面中使用它。

我还必须考虑缓存,使用 SWR 获取内部 API 的客户端非常简单,但服务器端我还不确定如何实现它。

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.

但是后来我在 Next.js 文档中读到,您不应该对getServerSideProps()中的所有 API 路由使用fetch() ) 。

您想直接在getServerSideProps中使用 API 路由中的逻辑,而不是调用您的内部 API。 这是因为getServerSideProps就像 API 路由一样在服务器上运行(从服务器向服务器本身发出请求将毫无意义)。 您可以从文件系统读取或直接从getServerSideProps访问数据库。 请注意,这仅适用于对内部 API 路由的调用——从getServerSideProps调用外部 API 非常好。

来自 Next.js getServerSideProps文档:

当您想从服务器获取数据时,可能很容易使用 API 路由,然后从getServerSideProps调用该 API 路由。 这是一种不必要且低效的方法,因为它会由于服务器上运行的getServerSideProps和 API 路由而导致发出额外的请求。

(...) 相反,直接将 API 路由中使用的逻辑导入getServerSideProps 这可能意味着直接从getServerSideProps内部调用 CMS、数据库或其他 API。

(请注意,使用getStaticProps / getStaticPaths方法时同样适用


这是一个小的重构示例,它允许您在getServerSideProps中重用来自 API 路由的逻辑。

假设您有这个简单的 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)
}

您可以将获取逻辑提取到单独的 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)
}

但也允许您在getServerSideProps中重新使用getData function 。

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

//...

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

您想直接在 getServerSideProps 中使用 API 路由中的逻辑,而不是调用内部 API。 这是因为 getServerSideProps 就像 API 路由一样在服务器上运行(从服务器向服务器本身发出请求将毫无意义)。 您可以从文件系统读取或直接从 getServerSideProps 访问数据库

我承认,你说的是对的,但问题仍然存在。 假设您已经编写了后端并且您的 api 是安全的,因此从安全和编写的后端提取逻辑似乎很烦人并且浪费时间和精力。 另一个缺点是,通过从后端提取逻辑,您必须重写自己的代码来处理错误并验证用户并验证您编写的后端中存在的用户请求。 我想知道是否可以在 nextjs 中调用 api 而无需从中间战中获取逻辑? 答案是肯定的,这是我的解决方案: 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() },
  };
}

重要注意事项:如果您在所有中间件中使用我的代码,请不要忘记使用await next()而不是next() ,否则会出错。 另一个解决方案: next connect 有run类似 mycode 的方法,但我个人遇到了一些问题; 这是它的链接: next connet run method to call next api's in serverSideProps

只需尝试使用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

请注意, fetch支持绝对 URL ,这就是我不喜欢使用它的原因。

PS 根据文档,您甚至可以将useSWRSSR一起使用。

暂无
暂无

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

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