[英]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 ingetServerSideProps()
.但是后来我在 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 bothgetServerSideProps
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 insidegetServerSideProps
.这可能意味着直接从
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 根据文档,您甚至可以将
useSWR
与SSR
一起使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.