REST Data Source
RESTDataSource 是负责从一个REST API获取数据。
安装
npm install apollo-datasource-rest
使用
const { RESTDataSource } = require('apollo-datasource-rest');
class MoviesAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://movies-api.example.com/';
}
async getMovie(id) {
return this.get(`movies/${id}`);
}
async getMostViewedMovies(limit = 10) {
const data = await this.get('movies', {
per_page: limit,
order_by: 'most_viewed',
});
return data.results;
}
}
HTTP Methods
class MoviesAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://movies-api.example.com/';
}
// an example making an HTTP POST request
async postMovie(movie) {
return this.post(
`movies`, // path
movie, // request body
);
}
// an example making an HTTP PUT request
async newMovie(movie) {
return this.put(
`movies`, // path
movie, // request body
);
}
// an example making an HTTP PATCH request
async updateMovie(movie) {
return this.patch(
`movies`, // path
{ id: movie.id, movie }, // request body
);
}
// an example making an HTTP DELETE request
async deleteMovie(movie) {
return this.delete(
`movies/${movie.id}`, // path
);
}
}
Intercepting fetches
在发送请求前截取请求,并进行修改,一般用于授权
class PersonalizationAPI extends RESTDataSource {
willSendRequest(request) {
request.headers.set('Authorization', this.context.token);
}
}
或者添加请求的参数
class PersonalizationAPI extends RESTDataSource {
willSendRequest(request) {
request.params.set('api_key', this.context.token);
}
}
Resolving URLs dynamically
get baseURL() {
if (this.context.env === 'development') {
return 'https://movies-api-dev.example.com/';
} else {
return 'https://movies-api.example.com/';
}
}
或者
async resolveURL(request: RequestOptions) {
if (!this.baseURL) {
const addresses = await resolveSrv(request.path.split("/")[1] + ".service.consul");
this.baseURL = addresses[0];
}
return super.resolveURL(request);
}
Accessing data sources from resolvers
const server = new ApolloServer({
typeDefs,
resolvers,
dataSources: () => {
return {
moviesAPI: new MoviesAPI(),
personalizationAPI: new PersonalizationAPI(),
};
},
context: () => {
return {
token: 'foo',
};
},
});
现在我们可以在context中使用dataSources了,没有把dataSources放在context中是避免循环依赖。
Query: {
movie: async (_source, { id }, { dataSources }) => {
return dataSources.moviesAPI.getMovie(id);
},
mostViewedMovies: async (_source, _args, { dataSources }) => {
return dataSources.moviesAPI.getMostViewedMovies();
},
favorites: async (_source, _args, { dataSources }) => {
return dataSources.personalizationAPI.getFavorites();
},
},
What about DataLoader?
用来批处理和缓存数据,减少重复请求。但是对于RESTAPI 一般没有太大用处。因为DataLoader的主要作用是批处理,而不是缓存。
Using Memcached/Redis as a cache storage backend
示例
const { RedisCache } = require('apollo-server-cache-redis');
const server = new ApolloServer({
typeDefs,
resolvers,
cache: new RedisCache({
host: 'redis-server',
// Options are passed through to the Redis client
}),
dataSources: () => ({
moviesAPI: new MoviesAPI(),
}),
});