gqlr v0.0.1

0

Monthly downloads

0th

Percentile

'GraphQL' Server in R

Server implementation of 'GraphQL' <http://facebook.github.io/graphql/>, a query language created by Facebook for describing data requirements on complex application data models. Visit <http://graphql.org> to learn more about 'GraphQL'.

Readme

gqlr: A GraphQL Implementation in R

Travis-CI Build Status Coverage Status CRAN_Status_Badge

Overview

GraphQL is a data "query language created by Facebook in 2015 for describing the capabilities and requirements of data models for client‐server applications". The GraphQL specification can be read on their hosted github. A cliff notes version is described on Facebook's github. To learn more about the GraphQL language, I highly recommend Facebook's public GraphQL website.

This package pulls inspiration from Facebook's graphql-js and Mathew Mueller's graph.ql. I wanted the full functionality of GraphQL, but I didn't want to force users to write full object definitions when can already be defined with GraphQL syntax.

gqlr merges R's rapid development speed with the consistent query language of GraphQL.

Installation

# The development version from GitHub:
# install.packages("devtools")
devtools::install_github("schloerke/gqlr")

If you encounter a clear bug, please file a minimal reproducible example on github.

Usage

Hello World

```{r, message = FALSE}

library(magrittr) library(gqlr)

schema <- " type Hello { world: String } schema { query: Hello } " %>% gqlr_schema()

execute_request("{world}", schema, initial_value = list(world = "Hi!"))

{

"data": {

"world": "Hi!"

}

}


### Star Wars

#### Star Wars Data

```r
add_human <- function(human_data, id, name, appear, home, friend) {
  human <- list(id = id, name = name, appearsIn = appear, friends = friend, homePlanet = home)
  # set up a function to be calculated if the field totalCredits is required
  human$totalCredits <- function(obj, args, schema) {
    length(human$appearsIn)
  }
  human_data[[id]] <- human
  human_data
}
add_droid <- function(droid_data, id, name, appear, pf, friend) {
  droid <- list(id = id, name = name, appearsIn = appear, friends = friend, primaryFunction = pf)
  # set extra fields manually
  droid$totalCredits <- length(droid$appearsIn)
  droid_data[[id]] <- droid
  droid_data
}

human_data <- list() %>%
  add_human("1000", "Luke Skywalker", c(4, 5, 6), "Tatooine", c("1002", "1003", "2000", "2001")) %>%
  add_human("1002", "Han Solo",       c(4, 5, 6), "Corellia", c("1000", "1003", "2001")) %>%
  add_human("1003", "Leia Organa",    c(4, 5, 6), "Alderaan", c("1000", "1002", "2000", "2001"))

droid_data <- list() %>%
  add_droid("2000", "C-3PO", c(4, 5, 6), "Protocol", c("1000", "1002", "1003", "2001")) %>%
  add_droid("2001", "R2-D2", c(4, 5, 6), "Astromech", c("1000", "1002", "1003"))

all_characters <- list() %>% append(human_data) %>% append(droid_data)
all_characters[[1]]
# $id
# [1] "1000"
#
# $name
# [1] "Luke Skywalker"
#
# $appearsIn
# [1] 4 5 6
#
# $friends
# [1] "Tatooine"
#
# $homePlanet
# [1] "1002" "1003" "2000" "2001"
#
# $totalCredits
# function (obj, args, schema)
# {
#     length(human$appearsIn)
# }
# <environment: 0x7fadd8ca2038>

Star Wars Schema

"
enum Episode { NEWHOPE, EMPIRE, JEDI }

interface Character {
  id: String!
  name: String
  friends: [Character]
  appearsIn: [Episode]
}

type Droid implements Character {
  id: String!
  name: String
  friends: [Character]
  appearsIn: [Episode]
  primaryFunction: String
}
type Human implements Character {
  id: String!
  name: String
  friends: [Character]
  appearsIn: [Episode]
  homePlanet: String
}

type Query {
  hero(episode: Episode): Character
  human(id: String!): Human
  droid(id: String!): Droid
}
# the schema type must be provided if a query or mutation is to be executed
schema {
  query: Query
}
" %>%
  gqlr_schema(
    Episode = list(
      resolve = function(episode_id, schema) {
        switch(as.character(episode_id),
          "4" = "NEWHOPE",
          "5" = "EMPIRE",
          "6" = "JEDI",
          "UNKNOWN_EPISODE"
        )
      }
    ),
    Character = list(
      resolve_type = function(id, schema) {
        ifelse(id %in% names(droid_data), "Droid", "Human")
      }
    ),
    Human = list(
      # Add a resolve method for type Human that takes in an id and returns the human data
      resolve = function(id, args, schema) {
        human_data[[id]]
      }
    ),
    Droid = list(
      # description for Droid
      description = "A mechanical creature in the Star Wars universe.",
      # Add a resolve method for type Droid that takes in an id and returns the droid data
      resolve = function(id, schema) {
        droid_data[[id]]
      }
    ),
    Query = function(null, schema) {
      list(
        # return a function for key 'hero'
        # the id will be resolved by the appropriate resolve() method of Droid or Human
        hero = function(obj, args, schema) {
          episode <- args$episode
          if (identical(episode, 5) || identical(episode, "EMPIRE")) {
            "1000" # Luke
          } else {
            "2001" # R2-D2
          }
        },
        # the id will be resolved by the Human resolve() method
        human = function(obj, args, schema) {
          args$id
        },
        # the id will be resolved by the Droid resolve() method
        droid = function(obj, args, schema) {
          args$id
        }
      )
    }
  ) ->
star_wars_schema

Star Wars Execution

# Use the resolve method to initialize the data
"
{
  hero {
    id
    name
    friends {
      id
      name
    }
  }
}
" %>%
  execute_request(star_wars_schema)
# {
#   "data": {
#     "hero": {
#       "id": "2001",
#       "name": "R2-D2",
#       "friends": [
#         {
#           "id": "1000",
#           "name": "Luke Skywalker"
#         },
#         {
#           "id": "1002",
#           "name": "Han Solo"
#         },
#         {
#           "id": "1003",
#           "name": "Leia Organa"
#         }
#       ]
#     }
#   }
# }


# Use variables...
"
query FetchSomeIDQuery($someId: String!) {
  human(id: $someId) {
    name
  }
}
" %>%
  execute_request(star_wars_schema, variables = list(someId = "1000"))
# {
#   "data": {
#     "human": {
#       "name": "Luke Skywalker"
#     }
#   }
# }


# Introspection
"
query IntrospectionTypeQuery {
  __type(name: \"Droid\") {
    kind
    name
    fields {
      name
    }
  }
}
" %>%
  execute_request(star_wars_schema)
# {
#   "data": {
#     "__type": {
#       "kind": "OBJECT",
#       "name": "Droid",
#       "fields": [
#         {
#           "name": "id"
#         },
#         {
#           "name": "name"
#         },
#         {
#           "name": "friends"
#         },
#         {
#           "name": "appearsIn"
#         },
#         {
#           "name": "primaryFunction"
#         },
#         {
#           "name": "__typename"
#         }
#       ]
#     }
#   }
# }

Star Wars Server

# R
server(star_wars_schema, log = TRUE)

Explore with curl


# GET R2-D2 and his friends' names
curl '127.0.0.1:8000/graphql?query=%7Bhero%7Bname%7D%7D&pretty=TRUE'
# {
#   "data": {
#     "hero": {
#       "name": "R2-D2"
#     }
#   }
# }

# POST for R2-D2 and his friends' names (no need to url escape the query)
curl --data '{"query":"{hero{name}}"}' '127.0.0.1:8000/graphql' # defaults to parse as JSON
# {"data":{"hero":{"name":"R2-D2"}}}

curl --data '{"query":"{hero{name}}"}' '127.0.0.1:8000/graphql' --header "Content-Type:application/json"
# {"data":{"hero":{"name":"R2-D2"}}}

curl --data '{hero{name}}' '127.0.0.1:8000/graphql' --header "Content-Type:application/graphql"
# {"data":{"hero":{"name":"R2-D2"}}}


# GET Schema definition
curl '127.0.0.1:8000/'
# enum Episode {
#   NEWHOPE
#   EMPIRE
#   JEDI
# }
#
# type Droid implements Character {
#   id: String!
#   name: String
#   friends: [Character]
#   appearsIn: [Episode]
#   primaryFunction: String
# }
#
# type Human implements Character {
#   id: String!
#   name: String
#   friends: [Character]
#   appearsIn: [Episode]
#   homePlanet: String
# }
#
# type Query {
#   hero(episode: Episode): Character
#   human(id: String!): Human
#   droid(id: String!): Droid
# }
#
# interface Character {
#   id: String!
#   name: String
#   friends: [Character]
#   appearsIn: [Episode]
# }
#
# schema {
#   query: Query
# }

Presentations

WOMBAT2016 in Melbourne, Australia; Feb 2016.

  • graphql
    • Bindings to libgraphqlparser for R.
    • gqlr uses this package to parse all requests strings and schema strings into json
  • ghql
    • General purpose GraphQL client
    • The ghql client could be used to submit requests to gqlr server to be executed

Functions in gqlr

Name Description
gqlr_schema Create Schema definitions
parse_ast Parse AST
as_R6 As R6
execute_request Execute GraphQL server response
server Run basic GraphQL server
ErrorList ErrorList
Schema GraphQL Schema object
No Results!

Last month downloads

Details

License MIT + file LICENSE
URL https://github.com/schloerke/gqlr, http://facebook.github.io/graphql/, http://graphql.org
BugReports https://github.com/schloerke/gqlr/issues
LazyData true
RoxygenNote 6.0.1
Encoding UTF-8
Collate 'AAA-utils.R' 'R6--aaa-utils.R' 'S3--aaa-setup.R' 'R6--definition.R' 'R6-3.1.1-types-scalars.R' 'R6-6.1-executing-requests.R' 'R6-Schema.R' 'R6z-from-json.R' 'graphql_json.R' 'R6-3.2-directives.R' 'gqlr_schema.R' 'R6-4-introspection.R' 'R6-6.2-executing-operations.R' 'R6-6.3-executing-selection-sets.R' 'R6-6.4-executing-fields.R' 'R6-7-response.R' 'R6-ErrorList.R' 'R6-ObjectHelpers.R' 'R6-Result.R' 'R6-VariableValdationHelper.R' 'S3-3.1.2.3-validation-object-type.R' 'S3-str.R' 'gqlr-package.R' 'server_jug.R' 'upgrade_query_remove_fragments.R' 'validation-arguments.R' 'validation-input-coercion.R' 'validation-selection-set-can-merge.R' 'validation-query.R'
NeedsCompilation no
Packaged 2017-06-07 20:36:46 UTC; barret
Repository CRAN
Date/Publication 2017-06-07 21:37:29 UTC

Include our badge in your README

[![Rdoc](http://www.rdocumentation.org/badges/version/gqlr)](http://www.rdocumentation.org/packages/gqlr)