我实现了RESTDataSource,但是当我在操场上尝试查询时,相同的查询永远不会被缓存,并且总是从REST端点获取。
这些教程说,使用RESTDataSource时,基本的缓存系统无需额外配置即可工作,但显然我缺少一些东西。什么会使缓存失败?
我的ApolloServer创建:
/* ... */
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => ({
comicVineAPI: new ComicVineAPI(),
firebaseAPI: new FirebaseAPI(getFirestore())
})
});
/* ... */
我对REST端点的调用(在我的API类中扩展了RESTDataSource):
/* ... */
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;
}
/* ... */
谢谢您的帮助!
答案 0 :(得分:0)
未缓存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数据源将缓存响应。
在缓存过期之前,对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
,由于缺少缓存头,阿波罗数据源将不会缓存响应。因此,每次发送graphql请求时,它都会调用REST API。
发送graphql请求后,检查Redis实例中的缓存:
127.0.0.1:6379> keys *
(empty list or set)
P.S。如果您的REST API位于Nginx服务器之后,则应在Nginx服务器上启用缓存控制。
源代码:https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/rest-api-caching