0.2.0 • Published 2 years ago

mercurius-auto-schema v0.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

mercurius-auto-schema

mercurius-auto-schema allows you to create a GraphQL schema and endpoint from Fastify own route definitions. By using this module, you would get a dynamic OpenAPI specification of your API that will be used as the basis for the GraphQL implementation.

The main difference with other similar approaches is that mercurius-auto-schema does not perform any HTTP requests but leverages the high-throughput fastify.inject() utility for in-process request processing.

Built upon:

This module is high level and it exposes all the behavior of its constituent parts. See the individual modules for various docs and explanations.

Install

npm i mercurius-auto-schema

Example

'use strict'

const fastify = require('fastify')()
const { printSchema } = require('graphql')

fastify.register(require('mercurius-auto-schema'), {
  definitions: {
    openapi: {
      info: {
        title: 'Test OpenAPI',
        description: 'testing the fastify openapi',
        version: '0.1.0'
      }
    },
    exposeRoute: true
  },
  graphql: {
    graphiql: true
  }
})

fastify.put('/some-route/:id', {
  schema: {
    description: 'post some data',
    tags: ['user', 'code'],
    summary: 'qwerty',
    params: {
      type: 'object',
      properties: {
        id: {
          type: 'string',
          description: 'user id'
        }
      }
    },
    body: {
      type: 'object',
      properties: {
        hello: { type: 'string' },
        obj: {
          type: 'object',
          properties: {
            some: { type: 'string' }
          }
        }
      }
    },
    response: {
      201: {
        description: 'Succesful response',
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}, (req, reply) => { reply.send({ hello: `Hello ${req.body.hello}` }) })

fastify.get('/user/:id', {
  schema: {
    description: 'get a user',
    params: {
      type: 'object',
      properties: {
        id: {
          type: 'string',
          description: 'user id'
        }
      }
    },
    response: {
      201: {
        description: 'Succesful response',
        type: 'object',
        properties: {
          name: { type: 'string' },
          companyId: { type: 'string' }
        }
      }
    }
  },
  links: {
    201: {
      company: {
        operationId: 'getCompany',
        parameters: {
          id: '$request.path.id'
        }
      }
    }
  }
}, () => 'hello world')

fastify.get('/company/:id', {
  schema: {
    operationId: 'getCompany',
    description: 'get a company',
    params: {
      type: 'object',
      properties: {
        id: {
          type: 'string',
          description: 'company id'
        }
      }
    },
    response: {
      201: {
        description: 'Succesful response',
        type: 'object',
        properties: {
          name: { type: 'string' }
        }
      }
    }
  }
}, () => 'hello world')

fastify.listen(3000, err => {
  if (err) throw err
  console.log(printSchema(fastify.graphql.schema))
})

This will print:

type Query {
  """
  get a company

  Equivalent to GET /company/{id}
  """
  company(
    """company id"""
    id: String!
  ): Company

  """
  get a user

  Equivalent to GET /user/{id}
  """
  user(
    """user id"""
    id: String!
  ): User
}

"""Succesful response"""
type Company {
  name: String
}

"""Succesful response"""
type User {
  company: Company
  companyId: String
  name: String
}

type Mutation {
  """
  post some data

  Equivalent to PUT /some-route/{id}
  """
  putSomeRouteId(
    """user id"""
    id: String!
    someRouteInput: SomeRouteInput
  ): SomeRoute2
}

"""Succesful response"""
type SomeRoute2 {
  hello: String
}

input SomeRouteInput {
  hello: String
  obj: ObjInput
}

input ObjInput {
  some: String
}

Authorization headers & JWT support

If your application requires authenticating with JWT using a bearer token scheme, you could specify a few options to create a smoother transformation. (If you are curious to what happens without these options, take a look at https://github.com/mcollina/openapi-graphql#authentication).

'use strict'

const fastify = require('fastify')
const auto = require('mercurius-auto-schema')
const JWT = require('fastify-jwt')

async function run () {
  const app = fastify({ logger: { level: 'error' } })

  app.register(JWT, {
    secret: 'CHANGEME!!'
  })

  app.register(auto, {
    definitions: {
      openapi: {
        info: {
          title: 'Test swagger',
          description: 'testing the fastify swagger api',
          version: '0.1.0'
        },
        components: {
          securitySchemes: {
            bearerAuth: {
              type: 'http',
              scheme: 'bearer',
              format: 'JWT'
            }
          }
        }
      }
    },

    // Disable the auth-wrapping objects
    viewer: false,

    // You can use customizeHttpRequest to add other headers as well, e.g. OpenTelemetry.
    customizeHttpRequest (opts, context) {
      opts.headers.authorization = context.reply.request.headers.authorization
      return opts
    }
  })

  app.get('/user/:id', {
    schema: {
      operationId: 'user',
      description: 'get a user',
      params: {
        type: 'object',
        properties: {
          id: {
            type: 'string',
            description: 'user id'
          }
        }
      },
      response: {
        200: {
          description: 'Succesful response',
          type: 'object',
          properties: {
            name: { type: 'string' },
            companyId: { type: 'string' }
          }
        }
      },
      security: [
        { bearerAuth: [] }
      ]
    }
  }, async (req) => {
    await req.jwtVerify()
    equal(req.user.name, 'foobar', 'jwt parsed correctly')
    equal(req.params.id, 'foo42', 'user id matches')

    return {
      companyId: 42,
      name: 'foo'
    }
  })

  await app.ready()

  {
    const query = 'query { user (id: "foo42") { name, companyId } }'

    const res = await app.inject({
      method: 'POST',
      url: '/graphql',
      headers: {
        Authorization: `Bearer ${app.jwt.sign({ name: 'foobar' })}`
      },
      body: {
        query
      }
    })

    console.log(res.json())
    // {
    //  data: {
    //    user: {
    //      name: 'foo',
    //      companyId: 42
    //    }
    //  }
    // }
  }
})

License

MIT