64

I am trying to start my nestJs server and It keeps giving me this error:

UnhandledPromiseRejectionWarning: Error: You must await server.start() before calling server.applyMiddleware() at ApolloServer

I'm not even sure where to debug from as I am still very new at NestJs and GraphQL.

1
  • 3
    We can't help you without knowing how you currently run those things.
    – CherryDT
    Jul 12, 2021 at 22:59

15 Answers 15

57

This is a known bug with an open issue and a merged PR to fix it. For now, you can downgrade to apollo-server-express@^2

2
  • Thank you very much. It is working fine now
    – Boladek
    Jul 13, 2021 at 22:17
  • 1
    Looks like there's a plan for releasing @nestjs/graphql@9 to fix this. This major bump, according to the issue, will be long before the @nestjs/core and @nestjs/common major bump to 9. But as for when, I couldn't say Jul 14, 2021 at 14:57
42

A complete working code is:

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

const app = express();

const typeDefs = `
    type Query{
        totalPosts: Int!
    }
`;
const resolvers = {
    Query: {
        totalPosts: () => 100,
    },
};
let apolloServer = null;
async function startServer() {
    apolloServer = new ApolloServer({
        typeDefs,
        resolvers,
    });
    await apolloServer.start();
    apolloServer.applyMiddleware({ app });
}
startServer();
const httpserver = http.createServer(app);

app.get("/rest", function (req, res) {
    res.json({ data: "api working" });
});

app.listen(4000, function () {
    console.log(`server running on port 4000`);
    console.log(`gql path is ${apolloServer.graphqlPath}`);
});
1
  • 3
    This works without changing the libray version but the line below is not needed: const httpserver = http.createServer(app);
    – Chad
    Aug 22, 2021 at 11:34
32

I faced this issue when upgrading Ben Awad's Graphql-Next-Typeorm[...] stack, simply adding an await to server start fixed the warnings

const apolloServer = new ApolloServer({
    introspection: true,
    schema: await buildSchema({
      resolvers: [__dirname + '/resolvers/**/*.js'],
      validate: false
    }),
    context: ({ req, res }) => ({
      req,
      res,
      redis: redisClient
    }),
    formatError
  });


// added this line
  await apolloServer.start();

  apolloServer.applyMiddleware({
    app,
    cors: false
  });
5
  • Also from Ben Awad's course. cors: false really helped, but why turn introspection off? Oct 23, 2021 at 18:57
  • 1
    @BurgerBurglar I believe your question is off-topic, instrospection is set to true, here is more info about introspection apollographql.com/docs/apollo-server/api/apollo-server/…
    – eliastouil
    Oct 23, 2021 at 21:24
  • I couldn't find this info in the migration documentation though
    – Eric Burel
    Nov 24, 2021 at 7:16
  • @EricBurel I'm not sure I understand what you mean or imply, can you please detail?
    – eliastouil
    Nov 24, 2021 at 17:08
  • Simply that the Apollo v3 migration documentation is not very explicit about this need to wait for "start", or at least I couldn't find the relevant part, but your answer is perfect
    – Eric Burel
    Nov 24, 2021 at 17:24
12

For Apollo Server Express 3.0 and above, you need to define an async function that takes in typeDefs and resolvers parameters, then assign the server to the same Apollo initialization as before as shown here

async function startApolloServer(typeDefs, resolvers){
    const server = new ApolloServer({typeDefs, resolvers})
    const app = express();
    await server.start();
    server.applyMiddleware({app, path: '/graphql'});
    
    app.listen(PORT, () => {
    console.log(`Server is listening on port ${PORT}${server.graphqlPath}`);
})
}

startApolloServer(typeDefs, resolvers);

0
5

downgrading is not the option (at least anymore)

here is the solution =>

https://javascriptsu.wordpress.com/2021/08/02/apollo-error-must-await-server-start/

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

server.start().then(res => {
 server.applyMiddleware({ app });
 app.listen({ port: 3000 }, () =>
   console.log("nice")
 )
})
1
4

You can put everything in an async function and execute the function in your server(app,index...).js. You may also check the npm package. https://www.npmjs.com/package/apollo-server-express For example:

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

async function startExpressApolloServer() {

    const { typeDefs } = require('./graphql/schemas/schema');
    const { resolvers } = require('./graphql/resolvers/resolver');

    const server = new ApolloServer({ typeDefs, resolvers });
    await server.start();

    const app = express();
    
    server.applyMiddleware({ app, path: '/api/graphql' });

    await new Promise(resolve => app.listen({ port: 3001 }, resolve));
    console.log(`Server ready at http://localhost:3001${server.graphqlPath}`);
    return { server, app };
}

startExpressApolloServer();
1
3

I had the same type of problem. I was using TypeScript, Express, ApolloServer. What I did-

async function a(){
  const server = new ApolloServer({ typeDefs, resolvers });
  await server.start();
  server.applyMiddleware({ app, path: '/graphql' });
}
a();
3

This is not a bug. As per the documentation, the Apollo server needs to be instantiated in an async function. This is the recommended setup for Apollo Express:

import { ApolloServer } from 'apollo-server-express';
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core';
import express from 'express';
import http from 'http';

async function startApolloServer(typeDefs, resolvers) {
  const app = express();
  const httpServer = http.createServer(app);
  const server = new ApolloServer({
    typeDefs,
    resolvers,
    plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
  });
  await server.start();
  server.applyMiddleware({ app });
  await new Promise(resolve => httpServer.listen({ port: 4000 }, resolve));
  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
}
2

There are already some great answers here. But we should know why and where we should call server.start(). From apollo docs -

Always call await server.start() before calling server.applyMiddleware and starting your HTTP server. This allows you to react to Apollo Server startup failures by crashing your process instead of starting to serve traffic.

1

One other option is to downgrade your apollo to any 2.x.x. It solved my problem

1

This is my working server:

import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import typeDefs from './schema';

const app = express();

const server = new ApolloServer({
  typeDefs,
  mocks: true
});

server.start().then(() => {
  server.applyMiddleware({
    app,
    cors: true,
  });
});

const PORT = 4000;

app.listen(PORT, () => {
  console.log(
    `GraphQL endpoint and playground accessible at http://localhost:${PORT}${server.graphqlPath}`,
  );
});
  

The key thing here is to wrap the "applyMiddleware" function call inside the "server.start" async function.

0

In v3, if you use apollo-server-express the start function is required https://www.apollographql.com/docs/apollo-server/api/apollo-server/#start.

You can do something like this.

const app = express()
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
....
export const startup = async () => {
  await server.start()
  server.applyMiddleware({ app, path: `/api/${configs.region}/graphql` })
  return app
}

// call startup in another file to get app

1
0

It is not ok to start the apollo server in advance. What happens with the case when I have to explicitly use http/https. Please see the following case:

  const server = new ApolloServer({
    typeDefs: [KeycloakTypeDefs, typeDefs], // 1. Add the Keycloak Type Defs
    schemaDirectives: KeycloakSchemaDirectives, // 2. Add the
    formatError: new ApolloErrorConverter(),
    resolvers: resolvers,
    context: ({ req }) => {
      return makeContextWithDependencies(req);
    }
  });
  server.applyMiddleware({ app });

  http.createServer(app).listen(config.server.port, os.hostname());
  const options = {
    key: fs.readFileSync(config.server.ssl.keyFile, "utf8"),
    cert: fs.readFileSync(config.server.ssl.certFile, "utf8"),
    passphrase: config.server.ssl.passphrase
  };
  https
    .createServer(options, app)
    .listen(config.server.securePort, os.hostname());
  console.log(
    "Server waiting for requests on ports: " +
      config.server.port +
      "," +
      config.server.securePort
  );
1
  • If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From Review
    – PaulS
    Oct 26, 2021 at 11:19
0

We must wait for the server to get ready before adding middleware to it.

    const app = express();
    const apolloServer = new ApolloServer({
        schema: await buildSchema({
            resolvers: [HelloResolver],
            validate: false,
        }),
    });
    
    await apolloServer.start(); // First start the server then apply middleware on it

    apolloServer.applyMiddleware({ app });

0

you can do like that, it works for me.

const server = new ApolloServer({ schema });
const startApollo = async () => {
  try {
    await server.start();
    server.applyMiddleware({ app, path: "/api"})
  } catch (error) {
    console.log(error);
  }
}
1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Mar 14, 2022 at 7:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.