无法让默认的Apollo Server缓存工作

3

我实现了一个RESTDataSource,但是当我在游乐场尝试查询时,相同的查询从未被缓存,总是从REST终点获取。

教程说,在使用RESTDataSource时,基本缓存系统应该可以在不需要额外配置的情况下工作,但显然我漏掉了什么。是什么导致了缓存失败?

我创建ApolloServer的代码:

/* ... */
const server = new ApolloServer({
    typeDefs,
    resolvers,
    dataSources: () => ({
        comicVineAPI: new ComicVineAPI(),
        firebaseAPI: new FirebaseAPI(getFirestore())
    })
});
/* ... */

我对REST端点的调用(在扩展了RESTDataSource的API类中):

/* ... */
async request(path, params = {}) {
    params.format = 'json';
    params.limit = 50;
    params.api_key = process.env.COMIC_VINE_API_KEY;
    const response = await this.get(`${path}?${this.buildQuery(params)}`);
    return response.status_code === 1 && response;
}
/* ... */

感谢您的帮助!

你是如何确定响应来自Comic Vine服务器而不是缓存的?请求URL(${path}?${this.buildQuery(params)})每次都相同吗? - Daniel Rearden
是的,当我不改变参数,只是再次执行查询时,请求URL是相同的。此外,Comic Vine API门户网站会对每个资源的请求次数进行计数,并在每个(相同的)请求之后增加一次计数。每个(相同的)请求也需要几秒钟的时间,我认为只有第一次执行应该需要时间,而随后的请求应该从缓存中立即获取。 - Silly-Blood
1个回答

1
REST API响应未被缓存的原因可能是“上游Web服务没有缓存头:cache-control”。您可以阅读此文章 Layering GraphQL on top of REST 了解更多信息。
“使用数据源时,HTTP请求将根据从REST API响应中返回的缓存头自动缓存。”
知道这些之后,我做了一个示例:

rest-api-server.ts:

import express from 'express';
import faker from 'faker';

function createServer() {
  const app = express();
  const port = 3000;

  app.get('/user', (req, res) => {
    res.sendFile('index.html', { root: __dirname });
  });

  app.get('/api/user', (req, res) => {
    console.log(`[${new Date().toLocaleTimeString()}] request user`);
    const user = { name: faker.name.findName(), email: faker.internet.email() };
    res.set('Cache-Control', 'public, max-age=30').json(user);
  });

  app.get('/api/project', (req, res) => {
    console.log(`[${new Date().toLocaleTimeString()}] request project`);
    const project = { name: faker.commerce.productName() };
    res.json(project);
  });
  return app.listen(port, () => {
    console.log(`HTTP server is listening on http://localhost:${port}`);
  });
}

if (require.main === module) {
  createServer();
}

export { createServer };

graphql-server.ts:

import { ApolloServer, gql } from 'apollo-server-express';
import { RESTDataSource } from 'apollo-datasource-rest';
import express from 'express';
import { RedisCache } from 'apollo-server-cache-redis';
import { Request } from 'apollo-server-env';

class MyAPI extends RESTDataSource {
  constructor() {
    super();
    this.baseURL = 'http://localhost:3000/api/';
  }
  public async getUser() {
    return this.get('user');
  }
  public async getProject() {
    return this.get('project');
  }
  protected cacheKeyFor(request: Request) {
    return request.url;
  }
}

const typeDefs = gql`
  type User {
    name: String
    email: String
  }
  type Project {
    name: String
  }
  type Query {
    user: User
    project: Project
  }
`;

const resolvers = {
  Query: {
    user: async (_, __, { dataSources: ds }: IAppContext) => {
      return ds.myAPI.getUser();
    },
    project: async (_, __, { dataSources: ds }: IAppContext) => {
      return ds.myAPI.getProject();
    },
  },
};

const dataSources = () => ({
  myAPI: new MyAPI(),
});

interface IAppContext {
  dataSources: ReturnType<typeof dataSources>;
}

const app = express();
const port = 3001;
const graphqlPath = '/graphql';
const server = new ApolloServer({
  typeDefs,
  resolvers,
  dataSources,
  cache: new RedisCache({
    port: 6379,
    host: '127.0.0.1',
    family: 4,
    db: 0,
  }),
});

server.applyMiddleware({ app, path: graphqlPath });

if (require.main === module) {
  app.listen(port, () => {
    console.log(`Apollo server is listening on http://localhost:${port}${graphqlPath}`);
  });
}

rest-api-server.ts的日志:

HTTP server is listening on http://localhost:3000
[2:21:11 PM] request project
[2:21:14 PM] request project
[2:21:25 PM] request user

对于/api/user,我设置了cache-control响应头。因此,当您向graphql-server.ts发送graphql请求时,MyAPI数据源将向REST API发送请求。在从REST API获取响应后,它检测到cache-control响应头,因此apollo Datasource将缓存响应。在缓存过期之前,下一个请求到graphql服务器将击中缓存。
发送graphql请求后,请检查Redis实例中的缓存。
root@d78b7c9e6ac2:/data# redis-cli
127.0.0.1:6379> keys *
1) "httpcache:http://localhost:3000/api/user"
127.0.0.1:6379> ttl httpcache:http://localhost:3000/api/user
(integer) -2
127.0.0.1:6379> keys *
(empty list or set)

对于 /api/project,由于缺少缓存头,apollo数据源将不会缓存响应。因此,每次发送graphql请求时,它都会调用REST API。
发送graphql请求后,请检查Redis实例中的缓存:
127.0.0.1:6379> keys *
(empty list or set)

提示:如果您的REST API在Nginx服务器后面,则应在Nginx服务器上启用缓存控制。

源代码:https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/rest-api-caching


很抱歉回复晚了,这非常有见地,非常感谢! - Silly-Blood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接