Catch the highlights of GraphQLConf 2023! Click for recordings. Or check out our recap blog post.
Docs
Source Handlers
OpenAPI / Swagger

OpenAPI / Swagger

image

This handler allows you to load remote or local OpenAPI (2/3) and Swagger schemas. This work originated from IBM Research, and is currently maintained by GraphQL Mesh.

For migrating from version < 0.32 see migration guide

You can import it using remote/local .json or .yaml.

To get started, install the handler library:

npm i @graphql-mesh/openapi

Now, you can use it directly in your Mesh config file / URL:

.meshrc.yaml
sources:
  - name: MyOpenapiApi
    handler:
      openapi:
        source: ./my-schema.json

Note that this handler is based on the JSON Schema handler - so it’s configurations will apply here as well.

Overriding default Query/Mutation operations classification

By default, all GET operations will be placed into Query fields and all other operations into Mutation fields. with this option, you can manually override this process. To switch between Query and Mutation operations, and vice versa, you need to define a rule per override, consisting of the OAS title, the path of the operation, the method of the operation and finally, the destination type (e.g., Query or Mutation). See the example below:

.meshrc.yaml
sources:
  - name: MyOpenapiApi
    handler:
      openapi:
        source: ./my-schema.json
        selectQueryOrMutationField:
          - fieldName: 'add_weather_forecast' # OAS field name
            type: Query # switch method POST from default Mutation into Query
          - fieldName: 'get_weather_forecast' # OAS field name
            type: Mutation # switch method GET from default Query into Mutation

Naming convention

We use operationId for the names, and aim to keep it as close as possible to origin.

Type naming

We adjust operationId only when necessary according to the GraphQL spec: - Chars (white space), ., /, : and - are replaced with _ (underscore) - Other chars which are not latin/digits are replaced with their char codes - If first char of the name is a digit, we prefix it with _ (GraphQL spec doesn’t allow that)

Unnamed types

We use path-based naming. So names could be structured like, for example, query_getUsers_items_firstName

Headers

Read about configuration and examples

Advanced cookies handling

When building a web application, cookies are often used for authentication for security reasons. On the other end, mobile applications tend to use an HTTP header.

This section shows how to configure GraphQL Mesh to accept either and use GraphQL Mesh to set/unset cookies on the login & logout mutations.

Accepting one of the cookies, header, or context value

We want to accept one of the following:

  • an accessToken cookie
  • an Authorization header
  • an authorization value available in context (e.g. set by a GraphQL auth plugin)

And transmit it to the Rest API as an Authorization header. GraphQL Mesh does not allow dynamic selection in the meshrc.yaml file, but that’s fine! We can use a bit of trickery.

.meshrc.yaml
sources:
  - name: Rest
    handler:
      openapi:
        source: ./openapi.yaml
        endpoint: '{env.REST_URL}/api/'
        operationHeaders:
          Authorization-Header: '{context.headers.authorization}'
          Authorization-Cookie: Bearer {context.cookies.accessToken}
 
# You can provide a custom fetch function to override the default fetch function
customFetch: ./src/custom-fetch.ts

Here in the meshrc.yaml configuration we store the cookie in Authorization-Cookie, and the header in Authorization-Header. To introduce the logic needed to generate the proper Authorization header for the Rest API, we need to implement a customFetch. It will replace the fetch used by GraphQL Mesh to call the Rest API.

custom-fetch.ts
import { MeshContext } from '@graphql-mesh/runtime'
import { fetch } from '@whatwg-node/fetch'
 
export default function (
  url: string,
  options: RequestInit = {},
  context?: MeshContext,
  info?: GraphQLResolveInfo
) {
  // Set Authorization header dynamically to context value, or input cookie, or input header, or from context.headers
  options.headers['authorization'] =
    context.authorization ||
    options.headers['authorization-cookie'] ||
    options.headers['authorization-header']
  // Clean up headers forwarded to the Rest API
  delete options.headers['authorization-cookie']
  delete options.headers['authorization-header']
  // Execute the fetch with the new headers
  return fetch(url, options)
}

Setting / Unsetting the cookie

Of course, using GraphQL Mesh as a Gateway for both the mobile application and web application is excellent. Still, there’s one thing missing: the setting of the cookie for the web application.

We need to access the HTTP response that is sent back to the client. Luckily, we can do so in additionalResolvers. So we need to create two new resolvers, one for login and one for logout, and manage the cookie in their code.

The first step is to edit the meshrc.yaml file, add this at the end:

meshrc.yaml
additionalTypeDefs: |
  extend type Mutation {
    login(credentials: Credentials!): String
    logout: Boolean
  }
additionalResolvers:
  - ./src/additional-resolvers.js

Then manage the cookie in the new resolvers:

resolvers.ts
// lifespan of our cookie
const oneYear = 365 * 24 * 3600
 
const resolvers = {
  Mutation: {
    async login(root, args, context, info) {
      // Call the Rest API's login operation
      const result = await context.Rest.Mutation.accountLogin({
        root,
        args: {
          credentials: args.credentials
        },
        context,
        info
      })
      // if `result` contains a JWT token, you could instead decode it and set `Expires`
      // to the JWT token's expiration date
      context.res.set(
        'Set-Cookie',
        `accessToken=${result}; Path=/; Secure; HttpOnly; Max-Age=${oneYear};`
      )
 
      return result
    },
    logout(root, args, { res }) {
      // use old date to unset cookie
      res.set(
        'Set-Cookie',
        `accessToken=logout; Path=/; Secure; HttpOnly; Expires=Thu, 1 Jan 1970 00:00:00 GMT;`
      )
 
      return true
    }
  }
}
 
module.exports = { resolvers }

Callbacks as Subscriptions

OpenAPI handler is able to process OAS Callbacks as GraphQL Subscriptions. It uses your PubSub implementation to consume the data. But you have to define webhooks for individual callbacks to make it work.

See Subscriptions & Webhooks to create an endpoint to consume a webhook. You should use the callback url as pubSubTopic in the webhook configuration.

Also see our example; Subscriptions Example with Webhooks.

Loading the sources from a CDN like GraphQL Hive or schema registry

GraphQL Mesh supports loading the sources from a CDN or schema registry. You can use the source property to load the schema from a CDN or schema registry.

.meshrc.yaml
sources:
  - name: MyApi
    handler:
      openapi:
        source: https://cdn.graphql-hive.com/asce7c12-753d-hive-bee-d7f2c803e232/sdl?ext=.graphql
        schemaHeaders:
          X-Hive-CDN-Key: 'aabTxbEyC78NvSPQNO+qLrrRnBvODJJ8k4sL/2EtIwc='

Examples

We have a lot of examples for OpenAPI Handler;

Config API Reference

  • source (type: String, required) - A pointer to your API source (Support both JSON and YAML) - could be a local file, remote file or url endpoint
  • fallbackFormat (type: String (json | yaml | js | ts)) - Format of the files referenced from the source file, for cases content type isn’t detected automatically
  • endpoint (type: String) - Specifies the URL on which all paths will be based on. Overrides the server object in the OAS.
  • schemaHeaders (type: JSON) - If you are using a remote URL endpoint to fetch your schema, you can set headers for the HTTP request to fetch your schema.
  • operationHeaders (type: JSON) - JSON object representing the Headers to add to the runtime of the API calls
  • ignoreErrorResponses (type: Boolean) - Responses are converted to a Union type grouping all possible responses. Applying this will ignore all responses with status code other than 2xx, resulting in simpler response types, usualy regular object type instead of union. Default: false
  • selectQueryOrMutationField (type: Array of Object) - Allows to explicitly override the default operation (Query or Mutation) for any OAS operation:
    • type (type: String (query | mutation | Query | Mutation), required)
    • fieldName (type: String, required)
  • queryParams (type: JSON) - JSON object representing the query search parameters to add to the API calls
  • timeout (type: Int) - Timeout for the HTTP request in milliseconds