mongodb - Next.js + Apollo + Mongodb:在 SSR 模式下无法访问 Apollo 上下文
问题描述
我正在尝试将 MongoDB 与 NextJS API 路由 Apollo 示例连接:
https://github.com/vercel/next.js/blob/canary/examples/api-routes-apollo-server-and-client
我找不到任何官方文档。所以我关注这个话题: Connect Apollo with mongodb 这个想法是从 ApolloServer 的上下文中连接 Mongodb
页面/api/graphql.js
import { ApolloServer } from 'apollo-server-micro';
import { schema } from '../../apollo/schema';
import { connectToDatabase } from '../../utils/mongodb';
const apolloServer = new ApolloServer({
schema,
context: async () => {
const { db } = await connectToDatabase();
return { db };
},
});
export const config = {
api: {
bodyParser: false,
},
};
export default apolloServer.createHandler({ path: '/api/graphql' });
“connectToDatabase”来自示例“with-mongo” https://github.com/vercel/next.js/blob/canary/examples/with-mongodb/util/mongodb.js
NextJS 中有 3 种渲染页面的方式。当我像这样连接我的数据库时(在 ApolloServer 上下文中),它在客户端渲染中工作得很好,但在静态和服务器端渲染中完全不行。我的解析器函数中的上下文是未定义的。
阿波罗/resolvers.js
export const resolvers = {
Query: {
async getAllCards(_parent, _args, _context, _info) {
console.log('_context resolver :>> ', _context);
const res = await _context.db.db
.collection('cards')
.find({})
.limit(20)
.toArray();
console.log('res :>> ', res);
return res;
},
},
};
那时我尝试关注这个主题 Next.js graphql context is empty {} on SSR getServerSideProps
按照这些步骤,我将修改 2 个文件:
页面/api/graphql
import { ApolloServer } from 'apollo-server-micro';
import { schema } from '../../apollo/schema';
import { connectToDatabase } from '../../utils/mongodb';
async function contextResolver(ctx) {
ctx.db = await connectToDatabase();
return ctx;
}
const apolloServer = new ApolloServer({
schema,
context: contextResolver,
});
export const config = {
api: {
bodyParser: false,
},
};
export default apolloServer.createHandler({ path: '/api/graphql' });
和
页面/explore_SSR.js
import gql from 'graphql-tag';
import Link from 'next/link';
import { initializeApollo } from '../apollo/client';
import { connectToDatabase } from '../utils/mongodb';
const Explore = () => {
return (
// UI Stuff
)
}
export async function getServerSideProps(ctx) {
console.log('ctx :>> ', ctx);
async function contextResolver(ctx) {
ctx.db = await connectToDatabase();
return ctx;
}
await contextResolver(ctx);
console.log('ctx after :>> ', ctx);
const apolloClient = initializeApollo(null, ctx);
await apolloClient.query({
query: gql`
query GetAllMementoQuery {
getAllMemento {
title
}
}
`,
});
return {
props: {
props: { initialApolloState: apolloClient.cache.extract() },
},
};
}
export default Explore;
console.log 的结果:console.log('ctx after :>> ', ctx);
ctx after :>> {
req: IncomingMessage {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: null,
ended: true,
endEmitted: false,
reading: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: false,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
_events: [Object: null prototype] { end: [Function: clearRequestTimeout] },
_eventsCount: 1,
_maxListeners: undefined,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
_paused: false,
_httpMessage: [ServerResponse],
timeout: 0,
[Symbol(async_id_symbol)]: 285916,
[Symbol(kHandle)]: [TCP],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: Timeout {
_idleTimeout: -1,
_idlePrev: null,
_idleNext: null,
_idleStart: 24100466,
_onTimeout: null,
_timerArgs: undefined,
_repeat: null,
_destroyed: true,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 290206,
[Symbol(triggerId)]: 290203
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: {
host: 'localhost:3000',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0',
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'accept-language': 'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
'accept-encoding': 'gzip, deflate',
referer: 'http://localhost:3000/explore_clientSide',
dnt: '1',
connection: 'keep-alive',
'upgrade-insecure-requests': '1',
'sec-gpc': '1'
},
rawHeaders: [
'Host',
'localhost:3000',
'User-Agent',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:88.0) Gecko/20100101 Firefox/88.0',
'Accept',
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language',
'fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding',
'gzip, deflate',
'Referer',
'http://localhost:3000/explore_clientSide',
'DNT',
'1',
'Connection',
'keep-alive',
'Upgrade-Insecure-Requests',
'1',
'Sec-GPC',
'1'
],
trailers: {},
rawTrailers: [],
aborted: false,
upgrade: false,
url: '/explore_SSR',
method: 'GET',
statusCode: null,
statusMessage: null,
client: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
_paused: false,
_httpMessage: [ServerResponse],
timeout: 0,
[Symbol(async_id_symbol)]: 285916,
[Symbol(kHandle)]: [TCP],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: Timeout {
_idleTimeout: -1,
_idlePrev: null,
_idleNext: null,
_idleStart: 24100466,
_onTimeout: null,
_timerArgs: undefined,
_repeat: null,
_destroyed: true,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 290206,
[Symbol(triggerId)]: 290203
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_consuming: false,
_dumped: false,
cookies: [Getter/Setter],
__NEXT_INIT_QUERY: {},
[Symbol(kCapture)]: false,
[Symbol(RequestTimeout)]: undefined
},
res: <ref *1> ServerResponse {
_events: [Object: null prototype] { finish: [Function: bound resOnFinish] },
_eventsCount: 1,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: false,
chunkedEncoding: false,
shouldKeepAlive: true,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: true,
sendDate: true,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: null,
_hasBody: true,
_trailer: '',
finished: false,
_headerSent: false,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: null,
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 8,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: true,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: [Server],
_server: [Server],
parser: [HTTPParser],
on: [Function: socketListenerWrap],
addListener: [Function: socketListenerWrap],
prependListener: [Function: socketListenerWrap],
_paused: false,
_httpMessage: [Circular *1],
timeout: 0,
[Symbol(async_id_symbol)]: 285916,
[Symbol(kHandle)]: [TCP],
[Symbol(kSetNoDelay)]: false,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: Timeout {
_idleTimeout: -1,
_idlePrev: null,
_idleNext: null,
_idleStart: 24100466,
_onTimeout: null,
_timerArgs: undefined,
_repeat: null,
_destroyed: true,
[Symbol(refed)]: false,
[Symbol(kHasPrimitive)]: false,
[Symbol(asyncId)]: 290206,
[Symbol(triggerId)]: 290203
},
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_header: null,
_keepAliveTimeout: 5000,
_onPendingData: [Function: bound updateOutgoingData],
_sent100: false,
_expect_continue: false,
statusCode: 200,
flush: [Function: flush],
write: [Function: write],
end: [Function: end],
on: [Function: on],
writeHead: [Function: writeHead],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: null
},
query: {},
resolvedUrl: '/explore_SSR',
locales: undefined,
locale: undefined,
defaultLocale: undefined,
db: {
client: MongoClient {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
s: [Object],
topology: [NativeTopology],
[Symbol(kCapture)]: false
},
db: Db {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
s: [Object],
serverConfig: [Getter],
bufferMaxEntries: [Getter],
databaseName: [Getter],
[Symbol(kCapture)]: false
}
}
}
但这对我不起作用。我的解析器的 _context 仍未定义。(apollo/resolvers.js 中的console.log:_context :>> 未定义)
也许我们不能将 Apollo 和数据库连接与 API 路由和服务器端渲染模式一起使用,或者我只是错过了一些大事。
知道如何实现 Mongodb 数据库连接以在任何渲染模式下从我的解析器访问它吗?
解决方案
推荐阅读
- c++ - 在 C++ 中读取 CSV 文件中的特定行
- arrays - Ruby - 将数组元素移动到给定位置
- ms-access - 更新事件后未触发更新查询 (MS Access)
- javascript - TypeScript:难以键入可以在对象内部的特定级别进行过滤同时保留其原始结构的函数
- express - 使用 azure 应用服务的快速会话未通过 https 保存/保留
- python - 当我的缩进正确时,为什么 python 给我一个错误的缩进?(已回答)
- python - 在Python中获取纬度和经度的集群中心点
- java - org.xml.sax.SAXParseException;系统标识
- c# - 如何在 ML.net 中使用 GloVe 词嵌入模型
- android - 在运行时从片段启用全屏无法与具有缺口的设备一起使用