graphql-dynamic

Dynamic, schema-less, directive-driven GraphQL

MIT License

Downloads
52
Stars
8

graphql-dynamic

dynamic, schema-less, directive-driven GraphQL

Table of Contents

Usage

npm install graphql-dynamic
import createLoader from 'graphql-dynamic'

const loader = createLoader()
const query = `
  {
    test @create(value: 1)
  }
`
const result = await loader.load(query) // output: { errors: [], infos: [], data: { test: 1 } }

Directives

  • graphql @
  • use
{
  # @create  use use  b 
  a @create(b: 1, use: "{ value: b }")
}

fetch|get|post url

{
  testUrlString @post(url: "http://example.com/api/name?a=1&b=2")
  testUrlObject
    @post(
      url: { host: "example.com", pathanme: "/api/name", query: { a: 1, b: 2 } }
    )
}

fetch|get|post headers

headers [[key, value]] { [key]: value }

graphql key json

{
  test
    @post(
      options: {
        headers: [
          ["Content-Type", "application/json"]
          ["Accept", "application/json"]
        ]
      }
    )
}

@post(url, body, options, bodyType, responseType)

post

  • url url string url object
  • bodypost
  • options, fetch(url, options) options headers fetch|get|post headers
  • bodyType post body json text
  • responseType post json text
{
  test
    @post(
      url: "/my/api"
      data: { a: 1, b: 2 }
      options: {
        headers: [
          ["Content-Type", "application/json"]
          ["Accept", "application/json"]
        ]
      }
      bodyType: "json"
      responseType: "json"
    )
}

@postAll(url, bodys, options, bodyType, responseType)

post Promise.all

  • url url string url object
  • bodys post body
  • options, fetch(url, options) options headers fetch|get|post headers
  • bodyType post body json text
  • responseType post json text

@get(url, query, options, responseType)

get

  • url url string url object
  • queryget querystring url ?
  • options, fetch(url, options) options headers fetch|get|post headers
  • responseType post json text
{
  test
    @get(
      url: "/my/api"
      query: { a: 1, b: 2 }
      options: {
        headers: [
          ["Content-Type", "application/json"]
          ["Accept", "application/json"]
        ]
      }
      bodyType: "json"
      responseType: "json"
    )
}

@getAll(url, querys, options, responseType)

get Promise.all

  • url url string url object
  • querys get querystring url ?
  • options, fetch(url, options) options headers fetch|get|post headers
  • responseType post json text

@fetch(url, options, bodyType, responseType)

fetch

  • url url string url object
  • options, fetch(url, options) options headers fetch|get|post headers
  • bodyType post body json text
  • responseType post json text
{
  test
    @fetch(
      url: "/my/api"
      options: {
        method: "POST"
        body: { a: 1, b: 2 }
        headers: [
          ["Content-Type", "application/json"]
          ["Accept", "application/json"]
        ]
      }
      bodyType: "json"
      responseType: "json"
    )
}

@create(value)

value

{
  number @create(value: 1)
  string @create(value: "1")
  object @create(value: { a: 1, b: 2 })
  array @create(value: [{ a: 1 }, { b: 2 }])
}
{
  number: 1,
  string: '1',
  object: { a: 1, b: 2 }
  array: [{ a: 1 }, {b: 2}]
}

@variable(name)

graphql

name fieldName

{
  a @create(value: 1) @variable #  a 
  b @create(value: $a) @variable(name: "c") #  a b  c
  c @create(value: $c) #  b  c
}

@map(to, ...context)

to js context

  • to
  • to key value
{
  a @create(value: 1) @map(to: "a + b", b: 1) # a  2
  objcet @create(value: { a: 1, b: 2 }) @map(to: "{ a: a + 1, b: b + n }", n: 1) # object  { a: 2, b: 3 }
  array @create(value: [{ a: 1 }, { a: 2 }]) @map(to: "{ a: a + 1 }") # array  [{ a: 2 }, { a: 3 }]
}

@filter(if, ...context)

if js context

  • if
  • if key value
{
  a @create(value: 1) @filter(if: "a > 1") # a 
  b @create(value: 1) @filter(if: "b === 1") # b  1
  objcet @create(value: { a: 1, b: 2 }) @filter(if: "b <= n", n: 2) # object  { a: 1, b: 2 }
  array @create(value: [{ a: 1 }, { a: 2 }]) @filter(if: "a < 2") # array  [{ a: 1 }]
}

@select(key)

key

key key

{
  a @create(value: { b: { c: { d: 1 } } }) @select(key: "d")
}
# 
# {
# 	a: {
# 		d: 1
# 	}
# }

@find(if)

if js context

  • if
  • if key value

filter find

{
  a @create(value: 1) @find(if: "a > 1") # a 
  b @create(value: 1) @find(if: "b === 1") # b  1
  objcet @create(value: { a: 1, b: 2 }) @find(if: "b <= n", n: 2) # object  { a: 1, b: 2 }
  array @create(value: [{ a: 1 }, { a: 2 }]) @find(if: "a < 2") # array  { a: 1 }
}

@extend(...object)

object

  • object
  • object key
  • extend
{
  a @extend(b: 1, c: 2) # a  { b: 1, c: 2 }
  b @create(value: { b: 0, d: 3 }) @extend(b: 1, c: 2) # b  { b: 1, c: 2, d: 3 }
  c @create(value: [{ b: 0, d: 3 }, { b: -1, d: 4 }]) @extend(b: 1, c: 2) # c  [{ b: 1, c: 2, d: 3 }, { b: 1, c: 2, d: 4 }]
}

@prepend(value)

value

{
  a @prepend(value: 1)
  b @prepend(value: "1")
  c @prepend(value: [1, 2])
  d @prepend(value: { value: 1 })
}

# 
# {
#   a: [1],
#   b: ['1'],
#   c: [1, 2],
#   d: [{ value: 1 }]
# }

{
  a @create(value: 0) @prepend(value: 1)
  b @create(value: "0") @prepend(value: "1")
  c @create(value: 0) @prepend(value: [1, 2])
  d @create(value: { value: 0 }) @prepend(value: { value: 1 })
  e @create(value: [0, 1, 2]) @prepend(value: [3, 4, 5])
}

# 
# {
#   a: [1, 0],
#   b: ['1', '0'],
#   c: [1, 2, 0],
#   d: [{ value: 1 }, { value: 0 }],
#   e: [3, 4, 5, 0, 1, 2]
# }

@append(value)

value @prepend

Api

graphql-dynamic graphql-anywhere api graphql-anywhere

createLoader(config)

createLoader graphql loader

config

  • variableTimeout graphql 3000
  • fetchTimeout fetch 3000

loader

  • load(query, variables?, context?, rootValue?)
    • query graphql () graphql document
    • variables graphql
    • context resolver context
    • rootValue resolver
    • load { errors, logs, data } promise
    • errors graphql
    • logs graphql fetch
    • data
  • use(...middlewares)
    • middlewares koa style : (ctx, next) -> promise
    • ctx config context
      • fieldName
      • rootValue
      • args
      • context
      • info isLeaf
      • result rootValue[fieldName] @create, @map
      • directive(directiveName, directiveHandler) directiveHandler params
      • fetch(url, options) fetch
      • error(error) errors
      • log(info) logs

loader.load loader.use loader.load loader.use

import createLoader from 'graphql-dynamic'

const loader = createLoader({
  variableTimeout: 3000,
  fetchTimeout: 3000
})

loader.use(async (ctx, next) => {
  let start = Date.now()
  await next()
  console.log('time', Date.now() - start)
})

const result = await loader.load(`{ test @create(value: 1) }`)
// { errors: [], logs: [], data: { test: 1 }}

ctx.directive @date

const moment = require('moment')

// @date(format, i18n)  moment 
loader.use((ctx, next) => {
  //  @date 
  ctx.directive('date', params => {
    if (!/number|string/.test(typeof ctx.result)) {
      return
    }
    let { format = 'YYYY/MM/DD', i18n = 'zh-cn' } = params
    let local = moment(ctx.result)
    if (i18n) local.locale(i18n)
    ctx.result = local.format(format)
  })
  return next()
})

expressjs

createGraphql(config) expressjs

config createLoader config graphql-playground

  • config.endpointgraphql-playground graphql server /graphql
import createGraphql from 'graphql-dynamic/express'
const express = require('express')
const app = express()

const playground = {
  'general.betaUpdates': false,
  'editor.cursorShape': 'line', // possible values: 'line', 'block', 'underline'
  'editor.fontSize': 14,
  'editor.fontFamily': `'Source Code Pro', 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace`,
  'editor.theme': 'light', // possible values: 'dark', 'light'
  'editor.reuseHeaders': true, // new tab reuses headers from last tab
  'request.credentials': 'omit', // possible values: 'omit', 'include', 'same-origin'
  'tracing.hideTracingResponse': true
}

const endpoint = '/graphql'
const router = createGraphql({ endpoint, playground })
app.use(endpoint, router)

// router.loader  loader