Subscriptions over HTTP multipart setup with Apollo Server + expressMiddleware

I can’t seem to find any documentation regarding the setup for Apollo Server to support subscriptions over HTTP multipart. The RFC specifies Apollo Server as an implementation, and the client docs talk about it too, but I can’t seem to find anything on how to properly set up the server side.

Currently I am observing that Apollo Client is sending the correct request + headers, but Apollo Server is trying to call the resolve function rather than the subscribe function, resulting in a failed subscription and null being sent back to the client.

Here is my setup (partially redacted):

// main.ts
async function bootstrap() {
  const app = express()
  const httpServer = http.createServer(app)

  app.use(cors({origin: process.env.CLIENT_URL}))
  app.use(express.json({limit: '50mb'}))
  app.use(express.urlencoded({extended: true}))
  app.use(cookieParser())

  /* GraphQL */
  const typeDefs = readFileSync('./graphql/schema.graphql').toString('utf-8')
  const resolvers = Resolvers({prisma, sync})
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    allowBatchedHttpRequests: true,
    plugins: [ApolloServerPluginDrainHttpServer({httpServer})]
  })
  await server.start()
  app.use('/graphql', expressMiddleware(server))

  /* start server */
  const port = process.env.PORT || 5050
  httpServer.listen(port, () => {
    if (process.env.NODE_ENV === 'development') {
      logger.info(`GraphQL Server available at http://localhost:${port}/graphql`)
    }
  })
}

bootstrap()
# schema.graphql

scalar DateTime
# ... redacted ...
type Subscription {
  time: DateTime
}
// resolvers.ts
export function Resolvers({prisma, sync}: {prisma: ExtPrismaClient; sync: PubSub}): Resolvers {
  return {
    DateTime: DateTimeResolver,
    Query: {
     // ... redacted ...
    },
    Subscription: {
      time: {
        subscribe: async function* () {
          // basic subscription example
          console.log("Subscribe called")
          while (true) {
            yield {time: new Date()}
            await new Promise(resolve => setTimeout(resolve, 1000))
          }
        }
      }
    }
  }
}

Note that the Subscribe called log message never displays.

Here is what the request / response is looking like.

Relevant request headers sent by Apollo Client:

{
  accept: 'multipart/mixed; boundary="graphql"; subscriptionSpec=1.0, application/json',
  connection: 'keep-alive'
}

Info from Apollo Server (logged using a custom plugin):

GraphQL request started.

GraphQL Operation: Subscription

GraphQL Query: 
subscription Subscription {
  time
}

GraphQL response being sent:
{
  "http": {
    "headers": {}
  },
  "body": {
    "kind": "single",
    "singleResult": {
      "data": {
        "time": null
      }
    }
  }
}

Any help is appreciated. Thanks!

Hey @yorkeJohn,

Let me help clarify the protocol support across the Apollo product suite today.

HTTP Multi-part is used by Apollo Clients to communicate with Apollo Router so that they do not have use other protocols that are not HTTP (aka websockets). Today all the Apollo Client libraries and Apollo Router on the backend have built-in support for these: https://www.apollographql.com/docs/graphos/routing/operations/subscriptions/multipart-protocol

If you are using Router/Federation then the Router has two protocols it can use to talk to downstream services (subgraphs). None of these are HTTP Multi-part. They are instead


What I believe you are asking for is support from Apollo Server in a non-federated setup to support the HTTP multi-part that Apollo Clients do support but only has built-in support from Apollo Router today.

The Solutions team has two examples that might help you see a working repo of the client connecting to Router to see the protocol in action.

However Apollo Server does not actually have multi-part support out of the box for subscriptions so that is something that you would have to handle via your own middleware. It does support multi-part for @defer queries.

Since we don’t have a included Subscription library you have to option to build/provide your own