next.jsapollo-servergraphql-subscriptions

Apollo Server with subscriptions used inside next.js api routes: websockets trouble


i try to setup GraphQL Subscriptions inside a next.js 9.x app. The app is totally fake, it is just for trying Apollo Server subscriptions. The "database" is just an array, where I push new users to it.

This is the code I got so far.

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" })

I run this subscription in localhost:3000/api/graphql:

subscription { newUser { id name } }

I get this error. I am not sure, where and how to fix this, as I can not find any documentation about this.

{ "error": "Could not connect to websocket endpoint ws://localhost:3000/api/graphql. Please check if the endpoint url is correct." }

I found out how to add the subscriptions path, as it complained about that before (was /graphql before). But still not working.


Solution

  • This is how I made it work.

    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;
    

    Just make sure that the websocket path works. https://www.websocket.org/echo.html