DEV Community

Diego Berolatti
Diego Berolatti

Posted on

The "Shared-Account" issue

I need to write about this issue. It involves in some way:

  • GraphQL (Apollo)
  • NodeJS
  • Cache
  • HTTP headers

Situation

I was working on this project a couple of months ago. I was developing the backend for a new website. It was the second time using Apollo as part of the backend tools (using GraphQL instead of REST allowed to separate more efficiently the frontend and back-end responsibilities and I love that). Everything was working fine until one day I found out something strange was happening with the service.

For some reason, the data from one specific user started to appear to all users in the service. I called it the "shared-account" issue since it made the server think that it was always the same user accessing the data.

By the way, something I didn't mention before is that the way the user authenticates to the service was with an external REST service. The client needed to send a key that was used internally in the GraphQL backend service to validate the user.

Task

The task was very simple. Find this embarrassing issue and solve it.

Action

First, I made sure the issue was not part of my imagination and I replicated it a couple of times until it was undeniable.

Then, I tried to isolate the behavior in a way I can replicate it every time I want. This is very important because it allows me to find the suspects.

So I started, by analyzing the interaction with the front end (sometimes the issue is the way the service is called), then only analyzing the backend behavior with an external client and finally adding logs to the specific pieces that were acting weird.

Changing the authentication header key in the client didn't change anything.

Restarting the server didn't fix the issue but only change the shared-account to the first user that used the service.

And then: I found the issue!

Apollo was saving in the cache of the server the authentication of a previous user. I didn't know why was saving it and why that user in particular. I wanted to understand how the cache worked in apollo so then I googled: "RESTDataSource apollo remove cache"

(RESTDataSource is the name of the apollo class that is in charge of the cache )

The first link sent me to this apollo issue from last year:

Disable cache in ApolloServer when using RESTDataSource #1562

I am using RESTDataSource to fetch data from REST api's but data is being cached.

How to disable cache since data keeps changing in thequery resolver.

Below is the node.js code.

   const app = express();
    
    const server = new ApolloServer({
      schema: schema,
      context: ({ req }) => ({
        token: req.headers.authorization,
      }),
      dataSources: () => apiDataSources,
    });
    
    server.applyMiddleware({ app });
    
    app.listen({ port: 4000 }, () => {
      console.log("Server is at http://localhost:4000/graphql");
    });

At least 4 kinds of solutions were suggested there:

  • Two ways of forcing the class to disable caching
    => Didn't want to do that so I didn't try them.

  • Other to check the response headers of the authentication service.
    => Apollo RESTDataSource uses the HTTP response "cache" headers to
    store the response in the cache, but in my case, the header response
    was "no-cache"

  • Others to change the TTL header to 60 seconds or 0 seconds or -1 second.
    => By default, Apollo uses 24 hours to store data in the cache, you can change this by sending the TTL down to 60 seconds. Some people suggested to use 0 seconds or even -1 second but this is not reliable.

None of this worked for me.

And lastly, one user mentioned that the context ( the object that contains the RESTDataSource) in apollo is instantiated per request.

That was it!

  const dataSources = {
      usersDatasource: new UsersDatasource(),
      spreadsheetDatasource: new SpreadsheetDatasource(database, logger),
  };

  ...
  introspection: false,
  playground: false,
  dataSources,
  },
  ...

I fixed it like this:

  ...
  introspection: false,
  playground: false,
  dataSources: () => {
    return {
      usersDatasource: new UsersDatasource(),
      spreadsheetDatasource: new SpreadsheetDatasource(database, logger),
    };
  },
  ...

Results

I learned a lot solving this issue and I hope you learned something too.

Diego

Top comments (0)