Apollo服务器,订阅在next.js api路由中使用:websockets问题

Apollo服务器,订阅在next.js api路由中使用:websockets问题,next.js,apollo-server,graphql-subscriptions,Next.js,Apollo Server,Graphql Subscriptions,我尝试在next.js 9.x应用程序中设置GraphQL订阅。该应用程序完全是假的,只是为了尝试阿波罗服务器订阅。“数据库”只是一个数组,我把新用户推到其中 这是我到目前为止得到的密码 import { ApolloServer, gql, makeExecutableSchema } from "apollo-server-micro" import { PubSub } from "apollo-server" const typeDefs = gql` type User {

我尝试在next.js 9.x应用程序中设置GraphQL订阅。该应用程序完全是假的,只是为了尝试阿波罗服务器订阅。“数据库”只是一个数组,我把新用户推到其中

这是我到目前为止得到的密码

import { ApolloServer, gql, makeExecutableSchema } from "apollo-server-micro"
import { PubSub } from "apollo-server"

const typeDefs = gql`
  type User {
    id: ID!
    name: String
    status: String
  }


  type Query {
    users: [User!]!
    user(id: ID!): User
  }

  type Mutation {
    addUser(id: String, name: String, status: String): User
  }

  type Subscription {
    newUser: User!
  }
`

const fakedb = [
  {
    id: "1",
    name: "myname",
    status: "active",
  },
]

const NEW_USER = "NEW_USER"

const resolvers = {
  Subscription: {
    newUser: {
      subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(NEW_USER),
    },
  },

  Query: {
    users: (parent, args, context) => {
      console.log(context)

      return fakedb
    },
    user: (_, { id }) => {
      console.log(id)
      console.log(fakedb)

      return fakedb.find((user) => user.id == id)
    },
  },
  Mutation: {
    addUser(_, { id, name, status }, { pubsub }) {
      console.log(pubsub)

      const newUser = {
        id,
        name,
        status,
      }

      pubsub.publish(NEW_USER, { newUser: newUser })

      fakedb.push(newUser)
      return newUser
    },
  },
}

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

const pubsub = new PubSub()
const apolloServer = new ApolloServer({
  // typeDefs,
  // resolvers,
  schema,
  context: ({ req, res }) => {
    return { req, res, pubsub }
  },
  introspection: true,
  subscriptions: {
    path: "/api/graphql",
    // keepAlive: 15000,
    onConnect: () => console.log("connected"),
    onDisconnect: () => console.log("disconnected"),
  },
})

export const config = {
  api: {
    bodyParser: false,
  },
}

export default apolloServer.createHandler({ path: "/api/graphql" })
我在localhost:3000/api/graphql:

订阅{ 新用户{ 身份证件 名称 } }

我得到这个错误。我不确定,在哪里以及如何解决这个问题,因为我找不到任何关于这个问题的文档

{ “错误”:“无法连接到websocket端点ws://localhost:3000/api/graphql。请检查端点url是否正确。” }


我发现了如何添加订阅路径,正如它之前抱怨的那样(was/graphql before)。但仍然不起作用。

这就是我如何使它起作用的

import { ApolloServer } from 'apollo-server-micro';
import schema from './src/schema';

const apolloServer = new ApolloServer({
  schema,
  context: async ({ req, connection }) => {
    if (connection) {
      // check connection for metadata
      return connection.context;
    }
    // get the user from the request
    return {
      user: req.user,
      useragent: req.useragent,
    };
  },

  subscriptions: {
    path: '/api/graphqlSubscriptions',
    keepAlive: 9000,
    onConnect: console.log('connected'),
    onDisconnect: () => console.log('disconnected'),
  },
  playground: {
    subscriptionEndpoint: '/api/graphqlSubscriptions',

    settings: {
      'request.credentials': 'same-origin',
    },
  },
});

export const config = {
  api: {
    bodyParser: false,
  },
};

const graphqlWithSubscriptionHandler = (req, res, next) => {
  if (!res.socket.server.apolloServer) {
    console.log(`* apolloServer first use *`);

    apolloServer.installSubscriptionHandlers(res.socket.server);
    const handler = apolloServer.createHandler({ path: '/api/graphql' });
    res.socket.server.apolloServer = handler;
  }

  return res.socket.server.apolloServer(req, res, next);
};

export default graphqlWithSubscriptionHandler;

只需确保websocket路径有效

我从@ordepim的答案中得到了灵感,我通过这种方式解决了热重新加载问题(我还添加了打字):

从“apollo server micro”导入{apollo server}
从“下一步”导入{NextApiRequest,nextapireresponse}
从“../../lib/schema”导入{schema}
//注意:每次热重新加载时都会出现此日志
console.log('正在创建服务器')
const阿波罗服务器=新阿波罗服务器({
模式,
上下文:异步({req,connection})=>{
如果(连接){
//检查元数据的连接
返回连接.context
}
//从请求中获取用户
返回{
用户:req.user,
useragent:req.useragent,
}
},
订阅:{
路径:'/api/graphqlSubscriptions',
keepAlive:9000,
onConnect:()=>console.log('connected'),
onDisconnect:()=>console.log('disconnected'),
},
游乐场:{
subscriptionEndpoint:“/api/graphqlSubscriptions”,
设置:{
'请求.凭据':'相同来源',
},
},
})
导出常量配置={
api:{
bodyParser:false,
},
}
类型CustomSocket=Exclude&{
服务器:参数[0]&{
阿波罗服务器?:阿波罗服务器
阿波罗:有吗
}
}
键入customnextapireresponse=nextapireresponse&{
套接字:自定义套接字
}
常量graphqlWithSubscriptionHandler=(
请求:下一个请求,
res:CustomnextapireResponse
) => {
const oldOne=res.socket.server.server
如果(
//我们需要比较旧的Apollo服务器和新的Apollo服务器,因为热重新加载后的服务器并不相等
老一套&&
oldOne!==阿波罗服务器
) {
控制台。警告('修复热重新加载!!!!!!!!!!!!!!!')
删除res.socket.server.server
}
if(!res.socket.server.server){
console.log(`*apollo服务器(重新)初始化*`)
apolloServer.installSubscriptionHandlers(res.socket.server)
res.socket.server.apolloServer=apolloServer
const handler=apolloServer.createHandler({path:'/api/graphql'})
res.socket.server.aspolloserverhandler=handler
//客户端会丢失旧的连接,但客户端可以重新连接
老一套
}
返回res.socket.server.ServerHandler(req,res)
}
导出默认graphqlWithSubscriptionHandler

此解决方案的一个不幸的缺点是,在设置了
res.socket.server.apolloServer
之后,热重新加载apollo服务器代码将不再工作。另一个怪癖是,在向服务器发出至少一个“正常”请求之前,订阅将无法开始工作。这是因为在发送正常请求之前,API路由处理程序没有执行。您能帮助我吗。