无法使默认的 Apollo Server 缓存工作
Can't get default Apollo Server cache to work
我实现了一个 RESTDataSource,但是当我在 playground 中尝试查询时,相同的查询永远不会被缓存,并且总是从 REST 端点获取。
教程说使用 RESTDataSource 时,基本缓存系统无需额外配置即可工作,但显然我遗漏了一些东西。什么可能导致缓存失败?
我的 ApolloServer 创建:
/* ... */
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => ({
comicVineAPI: new ComicVineAPI(),
firebaseAPI: new FirebaseAPI(getFirestore())
})
});
/* ... */
我对 REST 端点的调用(在我的 API class 扩展 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;
}
/* ... */
感谢您的帮助!
REST API 响应未被缓存的原因可能是 上游 Web 服务没有缓存 header: cache-control.您可以阅读这篇文章 Layering GraphQL on top of REST 了解更多信息。
With data sources, the HTTP requests are automatically cached based on the caching headers returned in the response from the 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
响应header。因此,当您向 graphql-server.ts
发送 graphql 请求时,MyAPI
数据源将发送
对 REST API 的请求。从 REST API 获得响应后,它检测到 cache-control
响应 header,因此 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
,由于缺少缓存header,apollo 数据源将不会缓存响应。因此,每次发送 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
我实现了一个 RESTDataSource,但是当我在 playground 中尝试查询时,相同的查询永远不会被缓存,并且总是从 REST 端点获取。
教程说使用 RESTDataSource 时,基本缓存系统无需额外配置即可工作,但显然我遗漏了一些东西。什么可能导致缓存失败?
我的 ApolloServer 创建:
/* ... */
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => ({
comicVineAPI: new ComicVineAPI(),
firebaseAPI: new FirebaseAPI(getFirestore())
})
});
/* ... */
我对 REST 端点的调用(在我的 API class 扩展 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;
}
/* ... */
感谢您的帮助!
REST API 响应未被缓存的原因可能是 上游 Web 服务没有缓存 header: cache-control.您可以阅读这篇文章 Layering GraphQL on top of REST 了解更多信息。
With data sources, the HTTP requests are automatically cached based on the caching headers returned in the response from the 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
响应header。因此,当您向 graphql-server.ts
发送 graphql 请求时,MyAPI
数据源将发送
对 REST API 的请求。从 REST API 获得响应后,它检测到 cache-control
响应 header,因此 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
,由于缺少缓存header,apollo 数据源将不会缓存响应。因此,每次发送 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