graphql-bridge
v1.1.0
Published
A package to easily integrate services/apis with your GraphQL resolvers
Downloads
27
Readme
GraphQL Bridge
With the classes provided by this repository you can easily integrate multiple REST and GraphQL endpoints in a uniform way.
This is especially useful when your application uses more than one API (for example Twitter and GitHub at the same time). Since most GraphQL documentation only describes how to wrap one single REST API I wrote this library to fix inconsistencies in using different services.
Usage
Basics
The two classes provided serve as base to inherit from. For each API to use in
your application you should create sub class of the respective class of the
library. A integration for an REST API needs to inherit from
GraphQLRestBridge
, a integration of an GraphQL API, you guessed it, from
GraphQLBridge
.
A simple example for the integration of the Google Maps GeoCoding API to reverse GeoCode an arbitrary address is shown below:
import { GraphQlRestBridge } from 'graphql-bridge';
export default class GraphQLGeoCodingRestBridge extends GraphQlRestBridge {
constructor() {
super({ key: 'ABC123' });
}
async reverseGeocode(address) {
const result = await super.request({
endpoint: 'https://maps.googleapis.com/maps/api/geocode/json',
data: {
address,
},
});
return result;
}
}
Using the constructor you can provide data (and headers) that should be included in every request.
/*
* Provides simple way to request data from an REST Endpoint
*
* @param {Object} defaultData What to include in every request
* @param {Object} defaultHeaders What headers to include in every request
*/
constructor(defaultData = {}, defaultHeaders = {}) {...}
It is important to note, that GraphQL subclasses will work in exactly the same way, as shown below:
export default class GraphQLGitHubBridge extends GraphQlBridge {
constructor() {
super(
'https://api.github.com/graphql',
{},
{ Authorization: `Bearer ${process.env.GITHUB_TOKEN}` }
);
}
async getUser(name) {
const result = await this.request({
query: `
query {
user(login: "${name}") {
email,
name,
}
}
`,
});
return result;
}
}
As a small difference, the first argument in the constructor is the URL of the API.
The actual usage of the library is illustrated in my repository GraphQL-Bridge-Demo (https://github.com/simonvomeyser/graphql-bridge-demo) where I integrated Twitter, GitHub and GoogleMaps.
super.request
This is the only function provided by both classes of the library and is used to make the actual request to the server. The possible arguments are as follows:
| Argument | RESTBridge | GraphQLBridge | Description | | -------- | ---------- | ------------- | ----------------------------------------------------------------------------------- | | endpoint | ✅ | ❌ | The enpoint to call an retrieve the data from. String | | method | ✅ | ❌ | Wrapper around the axios library, get,post,patch,put,delete. get is default. String | | data | ✅ | ❌ | The data to include in the requests body. Object | | query | ❌ | ✅ | The actual GraphQL query or mutation. String | | headers | ✅ | ✅ | The headers to include in the requests. Object | | nester | ✅ | ✅ | Nester function | | mapper | ✅ | ✅ | Mapper function | | filter | ✅ | ✅ | Filter function |
Nester
One of the three functions that can be passed to the super.request()
function.
Use to extract nested data. An simple example is shown below:
data => data.deep.nested.object.prop;
Filter
Offers the possibility to implement authorization or simple rules under which
conditions data should be returned or not. Should return true
or false
. The
example makes finding PersonX impossible.
user => {
if (user.name === 'PersonX') return false;
return true;
};
Mapper
Maps data from the service to own data so it can be used with GraphQL types. The example below shows mapping the returned data from the GoogleMaps API to an own GraphQL Type.
data => {
data.addressName = data.formatted_address;
data.lat = data.geometry.location.lat;
data.lng = data.geometry.location.lng;
return data;
};
Inside of a Resolver
After preparing an integration as described the created classes can simply be
used inside of a Resolver to greatly simplify interacting with services. The
variable GoogleGeoCodeIntegration
is an instance of the class we created
above.
geoCodedLocation: {
type: GoogleGeoCodeType,
resolve(parentValue) {
return GoogleGeoCodeIntegration.reverseGeocode(parentValue.location);
},
},