Apollo GraphQL Server Not Recovering From Crash

I’ve got an Apollo Server the works with redis. Every so often redis times out and that causes my apollo graphql server (running in docker) to crash and not start again.

I get the message “app crashed - waiting for file changes before starting”

How can I catch that error and just simply console log it and continue processing requests?

Here is what I have for my index.js (many details removed)

async function startApolloServer(typeDefs: any, resolvers: any) {
  
  const app = express();

  const httpServer = http.createServer(app);

  
 
  const cacheDefaultMs = parseInt(process.env.GRAPHQLSERVERREDISTIMEOUT ?? 0);
  const redisHost = process.env.REDISHOST ?? "localhost";
  const redisPort = process.env.REDISPORT ?? 6379;
  const graphqlCacheEnabled =
    process.env.GRAPHQLCACHEENABLED === "true" ?? false;

 
  const server = new ApolloServer<MyContext>({
    typeDefs,
    resolvers,
    csrfPrevention: true,
    cache: graphqlCacheEnabled === true ? new KeyvAdapter(keyV) : undefined, // for redis
    plugins: [
      responseCachePlugin(), // for redis
      ApolloServerPluginDrainHttpServer({ httpServer }),
    ],

   
  });
 

  require("dotenv").config();
  const version =
    process?.env?.RELEASEVERSION ?? "process?.env?.RELEASEVERSION";
  const releaseDate = process?.env?.RELEASEDATE ?? "process?.env?.RELEASEDATE";

  await server.start();
 
  );

  await new Promise<void>((resolve) =>
    httpServer.listen({ port: 4000 }, resolve)
  );
  console.log(`🚀 Server ready at http://localhost:4000/graphql`);
}

startApolloServer(typeDefs, resolvers);

We have a handy cache wrapper for exactly this, let me know if this isn’t the fix you’re looking for!

Thanks @trevor.scheer , I still get the same crash. I wonder if it has something to do with the responseCachePlugin I have in my server def? Here is the code I used from your reference. Seems pretty clear why it should work.

Also, if Redis is down, when I start my app, it crashes right away before I do any queries.

let keyVRedis: any;
  if (graphqlCacheEnabled === true) {

    const tempV = new Keyv(`redis://${redisHost}:${redisPort}`);
    keyVRedis = new ErrorsAreMissesCache(
      new KeyvAdapter(tempV),
    );
    //keyVRedis = new Keyv(`redis://${redisHost}:${redisPort}`);  // CODE FROM BEFORE ERRORSAREMISSEDCACHE
  }
const server = new ApolloServer<MyContext>({
    typeDefs,
    resolvers,
    csrfPrevention: true,
    cache: graphqlCacheEnabled === true ? keyVRedis : undefined, // for redis
    plugins: [
      responseCachePlugin(), // for redis
      ApolloServerPluginDrainHttpServer({ httpServer }),
    ],
  });

Package.json:

  "@apollo/server": "^4.7.5",
  "@apollo/server-plugin-response-cache": "^4.1.3",
  "@apollo/utils.keyvadapter": "^3.0.0",
  "@keyv/redis": "^2.7.0",
  "@prisma/client": "^4.16.2",
  "body-parser": "^1.20.2",
  "cors": "^2.8.5",
  "dataloader": "^2.2.2",
  "dotenv": "^16.3.1",
  "express": "^4.18.2",
  "graphql": "^16.7.1",
  "graphql-tag": "^2.12.6",
  "keyv": "^4.5.2",
  "njwt": "^2.0.0",
  "sharp": "^0.32.1",
  "sql-formatter": "^12.2.3"
 },

Got it. Thanks for the info, I was able to reproduce.

The cache wrapper I suggested is meant to handle errors in calling the cache methods, but not other connection errors (like on init, for example) and I’m not sure it should since I think that’s a separate concern.

The good news is you can handle those however you please. Keyvs are EventEmitters, which is how that error is being thrown in the first place. You can add an error event listener to your Keyv instance and handle those errors however you please.

tempV.on("error", (err) => {
  console.error(err);
});

Worked perfect! Thanks @trevor.scheer for the assist.

1 Like