Apollo-server: Using subscriptions with express on the same port

Created on 31 Oct 2018  路  6Comments  路  Source: apollographql/apollo-server


The current docs have a example on how to do this here of importance there is a note that says:

// 鈿狅笍 Pay attention to the fact that we are calling `listen` on the http server variable, and not on `app`.
httpServer.listen(PORT, () => {
  console.log(`馃殌 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  console.log(`馃殌 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})

Unfortunately using the httpServer to listen on a port disables all the express routing on that port.

I've been getting around this by doing something like:

httpServer.listen(PORT, () => {
  console.log(`馃殌 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  console.log(`馃殌 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})

app.listen(differentPort, () => console.log(`listening on http://localhost:${port}`));

But this isn't really ideal, is there a way to get the subscriptions and the express application to work on the same port?

Additional Information

I found what appears to be older documentation setting up the apollo-server to do this exact thing:

import express from 'express';
import bodyParser from 'body-parser';
import { graphqlExpress } from 'apollo-server-express';
import { createServer } from 'http';
import { execute, subscribe } from 'graphql';
import { PubSub } from 'graphql-subscriptions';
import { SubscriptionServer } from 'subscriptions-transport-ws';
import { myGraphQLSchema } from './my-schema';

const PORT = 3000;
const app = express();

app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema }));

const pubsub = new PubSub();
const server = createServer(app);

server.listen(PORT, () => {
    new SubscriptionServer({
      execute,
      subscribe,
      schema: myGraphQLSchema,
    }, {
      server: server,
      path: '/subscriptions',
    });
});

However, the apollo-server-express package no longer exports a graphqlExpress module and instead only has ApolloServer, ServerRegistration, registerServer. So far I've only used ApolloServer with installSubscriptionHandlers to setup basic subscriptions and haven't looked at the other methods. Is there a way to setup subscriptions on the same port as the express application or am I just chasing a dead end?

documentation 馃К subscriptions

Most helpful comment

Solution here: https://www.apollographql.com/docs/apollo-server/features/subscriptions.html#middleware

const http = require('http');
const { ApolloServer } = require('apollo-server-express');
const express = require('express');

const PORT = 4000;
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });

server.applyMiddleware({app})

const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);

// 鈿狅笍 Pay attention to the fact that we are calling `listen` on the http server variable, and not on `app`.
httpServer.listen(PORT, () => {
  console.log(`馃殌 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  console.log(`馃殌 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})

All 6 comments

Here's some working code, minus some custom details. I'm also using a customized ApolloServer class with changes to applyMiddleware, but it's not related to your issue. I noticed you need to get the order and pass the server instances in a specific way, or you get an error. I also moved some of methods from inside applyMiddleware (apollo upload/cors etc), just fyi.

  /* Request middleware */
  const middleware = [
    session(sessionConfig),
    cors(CORS_CONFIG),
    compression(COMPRESSION_CONFIG),
    json(),
    apolloUploadExpress(UPLOAD_OPTS),
    auth,
    getSchema,
  ]

  /* Instantiate app server, add request middleware */
  const app = express().use(GQL_SERVER_ENDPOINT, middleware)

  /* Instantiate GraphQL server */
  const server = new CustomApolloServer({
    /* GraphQL minimal public schema */
    schema: makeExecutableSchema(publicSchema),
    /* Apollo GraphQL datasources */
    dataSources: getDataSources,
    /* GraphQL context method, subscriptions */
    context: getContext,
    /* GraphQL subscriptions connection handlers */
    subscriptions: {
      onConnect: subConnect,
      onDisconnect: subDisconnect,
    },
  })

  /* Apollo GraphQL Server specific middleware */
  server.applyMiddleware({ app, path: GQL_SERVER_ENDPOINT })

  /* Create GraphQL subscriptions server */
  const httpServer = http.createServer(app)

  /* Attach subscription server to GraphQL server */
  server.installSubscriptionHandlers(httpServer)

  /* Start server */
  httpServer.listen(PORT, () => {
    console.log(`http://localhost:${PORT}${server.graphqlPath}`)
    console.log(`ws://localhost:${PORT}${server.subscriptionsPath}`)
  })

If you need to make more advanced changes, you would need to customize applyMiddleware and the code the instantiates the subscription server. You can find graphqlExpress exported at the below path, but It's a bit more complicated in 2.0. I noticed the order, and how the servers are passed in the above code is important. Does this help?

import { graphqlExpress } from 'apollo-server-express/dist/expressApollo'

As part of debugging a similar issue I wrote a minimal Express + Apollo server app with subscriptions, you can find it here: https://github.com/gforge/subscription_example It seems to work with shared port. Hope it helps!

Solution here: https://www.apollographql.com/docs/apollo-server/features/subscriptions.html#middleware

const http = require('http');
const { ApolloServer } = require('apollo-server-express');
const express = require('express');

const PORT = 4000;
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });

server.applyMiddleware({app})

const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);

// 鈿狅笍 Pay attention to the fact that we are calling `listen` on the http server variable, and not on `app`.
httpServer.listen(PORT, () => {
  console.log(`馃殌 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  console.log(`馃殌 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})

Solution here: https://www.apollographql.com/docs/apollo-server/features/subscriptions.html#middleware

const http = require('http');
const { ApolloServer } = require('apollo-server-express');
const express = require('express');

const PORT = 4000;
const app = express();
const server = new ApolloServer({ typeDefs, resolvers });

server.applyMiddleware({app})

const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);

// 鈿狅笍 Pay attention to the fact that we are calling `listen` on the http server variable, and not on `app`.
httpServer.listen(PORT, () => {
  console.log(`馃殌 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  console.log(`馃殌 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})

Worked a treat! Thanks!

@b3none i tried the above as i am recently looking to subscription. No luck i have been stuck with this . server runs fine. but the subscribe method is never called.
Posted the query in stackoverflow also no answer yet .

https://stackoverflow.com/questions/62835253/apollo-server-subscription-subscribe-method-is-never-called

having similar issues as @khirodAsurion. Everything seems to be working fine except for the subscribe function on the resolver for my subscription is not running. I logged in resolve function to verify this. Logging in the onConnect function suggests that client is successfully connecting over websocket. try catch block around pubsub.publish suggests it is not throwing an error. Result of await pubsub.publish is undefined. I have a single PubSub instance. Any ideas? :/

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jpcbarros picture jpcbarros  路  3Comments

nevyn-lookback picture nevyn-lookback  路  3Comments

danilobuerger picture danilobuerger  路  3Comments

manuelfink picture manuelfink  路  3Comments

bryanerayner picture bryanerayner  路  3Comments