UNPKG

6.53 kBMarkdownView Raw
1# Apollo REST Data Source
2
3This package exports a ([`RESTDataSource`](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-datasource-rest)) class which is used for fetching data from a REST API and exposing it via GraphQL within Apollo Server.
4
5## Documentation
6
7View the [Apollo Server documentation for data sources](https://www.apollographql.com/docs/apollo-server/features/data-sources/) for more details.
8
9## Usage
10
11To get started, install the `apollo-datasource-rest` package:
12
13```bash
14npm install apollo-datasource-rest
15```
16
17To define a data source, extend the [`RESTDataSource`](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-datasource-rest) class and implement the data fetching methods that your resolvers require. Data sources can then be provided via the `dataSources` property to the `ApolloServer` constructor, as demonstrated in the _Accessing data sources from resolvers_ section below.
18
19Your implementation of these methods can call on convenience methods built into the [RESTDataSource](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-datasource-rest) class to perform HTTP requests, while making it easy to build up query parameters, parse JSON results, and handle errors.
20
21```javascript
22const { RESTDataSource } = require('apollo-datasource-rest');
23
24class MoviesAPI extends RESTDataSource {
25 constructor() {
26 super();
27 this.baseURL = 'https://movies-api.example.com/';
28 }
29
30 async getMovie(id) {
31 return this.get(`movies/${id}`);
32 }
33
34 async getMostViewedMovies(limit = 10) {
35 const data = await this.get('movies', {
36 per_page: limit,
37 order_by: 'most_viewed',
38 });
39 return data.results;
40 }
41}
42```
43
44### HTTP Methods
45
46The `get` method on the [RESTDataSource](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-datasource-rest) makes an HTTP `GET` request. Similarly, there are methods built-in to allow for POST, PUT, PATCH, and DELETE requests.
47
48```javascript
49class MoviesAPI extends RESTDataSource {
50 constructor() {
51 super();
52 this.baseURL = 'https://movies-api.example.com/';
53 }
54
55 // an example making an HTTP POST request
56 async postMovie(movie) {
57 return this.post(
58 `movies`, // path
59 movie, // request body
60 );
61 }
62
63 // an example making an HTTP PUT request
64 async newMovie(movie) {
65 return this.put(
66 `movies`, // path
67 movie, // request body
68 );
69 }
70
71 // an example making an HTTP PATCH request
72 async updateMovie(movie) {
73 return this.patch(
74 `movies`, // path
75 { id: movie.id, movie }, // request body
76 );
77 }
78
79 // an example making an HTTP DELETE request
80 async deleteMovie(movie) {
81 return this.delete(
82 `movies/${movie.id}`, // path
83 );
84 }
85}
86```
87
88All of the HTTP helper functions (`get`, `put`, `post`, `patch`, and `delete`) accept a third options parameter, which can be used to set things like headers and referrers. For more info on the options available, see MDN's [fetch docs](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters).
89
90### Intercepting fetches
91
92Data sources allow you to intercept fetches to set headers, query parameters, or make other changes to the outgoing request. This is most often used for authorization or other common concerns that apply to all requests. Data sources also get access to the GraphQL context, which is a great place to store a user token or other information you need to have available.
93
94You can easily set a header on every request:
95
96```javascript
97class PersonalizationAPI extends RESTDataSource {
98 willSendRequest(request) {
99 request.headers.set('Authorization', this.context.token);
100 }
101}
102```
103
104Or add a query parameter:
105
106```javascript
107class PersonalizationAPI extends RESTDataSource {
108 willSendRequest(request) {
109 request.params.set('api_key', this.context.token);
110 }
111}
112```
113
114If you're using TypeScript, make sure to import the `RequestOptions` type:
115
116```javascript
117import { RESTDataSource, RequestOptions } from 'apollo-datasource-rest';
118
119class PersonalizationAPI extends RESTDataSource {
120 baseURL = 'https://personalization-api.example.com/';
121
122 willSendRequest(request: RequestOptions) {
123 request.headers.set('Authorization', this.context.token);
124 }
125}
126```
127
128### Resolving URLs dynamically
129
130In some cases, you'll want to set the URL based on the environment or other contextual values. You can use a getter for this:
131
132```javascript
133get baseURL() {
134 if (this.context.env === 'development') {
135 return 'https://movies-api-dev.example.com/';
136 } else {
137 return 'https://movies-api.example.com/';
138 }
139}
140```
141
142If you need more customization, including the ability to resolve a URL asynchronously, you can also override `resolveURL`:
143
144```javascript
145async resolveURL(request: RequestOptions) {
146 if (!this.baseURL) {
147 const addresses = await resolveSrv(request.path.split("/")[1] + ".service.consul");
148 this.baseURL = addresses[0];
149 }
150 return super.resolveURL(request);
151}
152```
153
154### Accessing data sources from resolvers
155
156To give resolvers access to data sources, you pass them as options to the `ApolloServer` constructor:
157
158```javascript
159const server = new ApolloServer({
160 typeDefs,
161 resolvers,
162 dataSources: () => {
163 return {
164 moviesAPI: new MoviesAPI(),
165 personalizationAPI: new PersonalizationAPI(),
166 };
167 },
168 context: () => {
169 return {
170 token: 'foo',
171 };
172 },
173});
174```
175
176Apollo Server will put the data sources on the context for every request, so you can access them from your resolvers. It will also give your data sources access to the context. (The reason for not having users put data sources on the context directly is because that would lead to a circular dependency.)
177
178From our resolvers, we can access the data source and return the result:
179
180```javascript
181 Query: {
182 movie: async (_source, { id }, { dataSources }) => {
183 return dataSources.moviesAPI.getMovie(id);
184 },
185 mostViewedMovies: async (_source, _args, { dataSources }) => {
186 return dataSources.moviesAPI.getMostViewedMovies();
187 },
188 favorites: async (_source, _args, { dataSources }) => {
189 return dataSources.personalizationAPI.getFavorites();
190 },
191 },
192```
193
194### Implementing custom metrics
195
196By overriding `trace` method, it's possible to implement custom metrics for request timing.
197
198See the original method [implementation](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-datasource-rest/src/RESTDataSource.ts) or the reference.