Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

gql-gateway

License

NotificationsYou must be signed in to change notification settings

segpacto/gql-gateway

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Description

This module provides a GraphQL Gateway that allows the interaction with Swagger based REST APIs, by autogenerating and merging their GraphQL schemas. 🚀
Through this gateway, it is possible to easily establish aggregations between the downstream REST services using GraphQL generated types, queries and mutations.

Related topics

How this GraphQL-Gateway actually works?

  1. Read and parse the Swagger specifications from all given endpoints.
  2. For each Swagger specification auto-generate the GraphQL Types, Queries and Mutations; as well as auto-generate the APIs based resolvers.
  3. Merge our local GraphQl definitions containing the aggregations and extensions along with the previous generated schemas.
  4. Serve an Apollo GraphQl server with all agreggations.

Getting started

Installation

npminstall--savegql-gateway

Getting started

Basic - Serve a basic GraphQL Gateway from public services

constgateway=require('gql-gateway')constendpointsList=[{name:'petstore_service',url:'https://petstore.swagger.io/v2/swagger.json'},{name:'fruits_service',url:'https://api.predic8.de/shop/swagger'}]gateway({ endpointsList}).then(server=>server.listen(5000)).then(console.log('Service is now running at port: 5000')).catch(err=>console.log(err))

Advanced - Adding aggregations

constgateway=require('gql-gateway')constlocalSchema=`  extend type Order {    pet: Pet  }`constresolvers={/*  Query : {     ....  }  */Order:{pet:{fragment:'... on Order {petId}',asyncresolve(order,args,context,info){constschema=awaitcontext.resolveSchema('pet_service')returninfo.mergeInfo.delegateToSchema({          schema,operation:'query',fieldName:'getPetById',args:{petId:order.petId},          context,          info})}}}}constconfig={port:5000,playgroundBasePath:'gql-gateway'}constendpointsList=[{name:'pet_service',url:'https://petstore.swagger.io/v2/swagger.json'}]constapolloServerConfig={playground:{endpoint:config.playgroundBasePath}}gateway({ resolvers, localSchema, endpointsList, apolloServerConfig}).then(server=>server.listen(config.port)).then(console.log(`Service is now running at port:${config.port}`)).catch(err=>console.log(err))

What just happened?

  • OnlocalSchema we declare the aggregations that we would like to have by extending the original schemas (to get the original schemas, queries and mutations it is recommended to publish the service and then take a look at them before start adding aggregations).
  • Onresolvers we declare the way how to resolve the modelOrder, for this we use graphqldelegations, where we specify on which of the autogenerated queries or mutations we relay to obtain thepet property inOrder, in this casegetPetById.

Note that on the fragment part we declarepetId as required field to obtain thepet property, sopetId is going to be injected from theOrder to the resolver even if it haven't been requested originally.

Configuration options explained

NameDefaultDescription
localSchemaemptySchema that contains the aggregations that we want to establish between the REST API services
resolversemptyResolvers that implement delegation. See samples above
endpointsListrequiredContains a list ofjson swagger endpoints where to retrieve definitions to build the graphql schemas.Minimum one element
apolloServerConfigemptyApollo Server configuration (https://www.apollographql.com/docs/apollo-server/api/apollo-server/#apolloserver)
contextConfigemptyObject that contains middlewares and also used to inject data into the Context (https://www.apollographql.com/docs/apollo-server/api/apollo-server/#apolloserver)
loggerconsoleDefault logger is the console

Format oflocalSchema parameters:

NameDefaultDescription
namerequiredIs used to identify the service
urlrequiredUrl of the service swagger injson format
headersemptyHeaders passed to request the json swagger service, in case any kind of particular auth is needed
onLoadedemptyFunction that process the swaggerJson once is loaded so changes on the flight can be introduced. If passedMust return the swaggerJson back

Format of functiononLoaded parameters:

NameDefaultDescription
swaggerJsonSwagger JSON schemaContains the loaded Swagger Json schema
serviceobjectContains thelocalSchema that was loaded

onLoadedfunction Ex :

constonLoaded=(swaggerJson,service)=>{swaggerJson.schemes=['http','https']returnswaggerJson}constendpointsList=[{name:'pet_service',url:'https://petstore.swagger.io/v2/swagger.json', onLoaded}]

Using theapolloServerConfig parameter:

constapolloServerConfig={playground:{endpoint:config.playgroundBasePath}}

Technical Explanation

Below, we describe how to interact between services swagger based using agreggations(relations).
In this example we take theUser andProduct services as example.

TheUser service:

...paths:    "/users":        get:            description: "Return an Array of existing users"            responses:                '200':                    description: "successful operation"                    schema:                        type: array                        items:                              "$ref": "#/definitions/User"...definitions :    User:        type: object        properties:            userId:                type: string            firstname:                type: string            lastname:                type: string...

TheProduct service:

...paths:  paths:    '/products/{userId}':      get:        tags:          - Product        parameters:          - name: userId            in: path            description: ID of the user to fetch last products            required: true            type: string        summary: Return a summary of the last products        description: Return a sumary of the user products        responses:          '200':            description: successful operation            schema:                type: array                items:                      "$ref": "#/definitions/Product"...definitions :    Product:        type: object        properties:            productId:                type: string            userId:                type: string            name:                type: string            type:                type: string...

Once the graphql gateway read from those services their swagger specification, our server generates the following:

type Queries {    get_products_userId(userId: String!): Products!    get_users(): [User]!}

Custom aggregations / relations

The next step is to extend the GraphQL definitions to introduce our custom global aggregations:

  • ExtendGraphQl Types definitions:
    extend type User {        products: Product    }    # You can always declare the relation in one direction    extend type Product {        user: User    }

This will automatically indicate to the GraphQl server that the TypeUser will have another field named products, actually theProduct service relation.

  • Finally, extendGraphQl resolvers:
User:{products:{fragment:'... on User {userId}',asyncresolve(user,args,context,info){returninfo.mergeInfo.delegateToSchema({schema:userSchema,operation:'query',fieldName:'get_products_userId',args:{userId:user.userId// here we hook the relation identifier},          context,          info})}}},
  • And now we can magically query:
query {  get_users {    firstname,    lastname,    products {      name,      type    }  }}

[8]ページ先頭

©2009-2025 Movatter.jp