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
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
${path}?${this.buildQuery(params)}
)每次都相同吗? - Daniel Rearden