0.0.4 • Published 7 years ago

graphdl v0.0.4

Weekly downloads
-
License
MIT
Repository
-
Last release
7 years ago

GraphDL

Graph Description Language is a GraphQL Schema Definition Language / syntax that enables terse GraphQL Schema creation with far fewer lines of code.

Note: this is experimental and for learning-purposes. For fullest GraphQL schema language implementation, use buildSchema() from the graphql npm library. More info: http://graphql.org/learn/schema/.

What? Why?

GraphQL is a pretty cool piece of tech that allows application clients (browsers, native apps, etc) to query for data from a server without making multiple RESTful requests. Imagine querying a GitHub user, all her/his projects, and each of those projects contributors from the GitHub API. If I were pulling my GitHub information (username @matthiasak), I would make these requests:

The data being requested then, by you poor programmer, must be "stitched" back together into a usable data structure after waiting potentially 30 seconds for all your data to be requested:

The architecture of all the data you requested:

+---------------------------+
|                           |
|     users/matthiasak      +-----------------+
|                           |                 |
+---------------------------+                 |
                                              |
                                              |
                                     +--------v-------+
                                     |                |
                                     |                |
                                     |matthiasak/repos|
                                     |                |
                                     |                |
                                    ++------+-+-+-----+
                                    |       | | |     |
                                    |       | | |     |
                                    |       | | |     |
                                    |       | | |     |
                                    |       | | |     |
                                    |       | | |     |
+----------------+<-----------------+       v v v     +------------->+----------------------+
|                |                      +---+-+-+---+                |                      |
|      repo1     |                      |    ...    |                |      repoN           |
+----+-----------+                      +---+--+--+-+                +-------+--------------+
     |                                      |  |  |                          |
     |                                      |  |  |                          |
     |                                      |  |  |                          |
+----v-----------+                      +---v--v--v------+           +-------v--------+
|                |                      |                |           |                |
|  contributors  |                      |  contributors  |           |  contributors  |
+----------------+                      +----------------+           +----------------+

For every user there's 2 requests, for each repo there's 2 requests, so for a user with 30 repos that means we're looking at 2 + 30*2 = 62 JSON requests just for this data (barring some special &include=... features of some RESTful APIs).

With GraphQL, we make only one request, instead, and we can configure exactly what fields of each record we need:

# graphql query
query getContributorGraph($user: String) {
	user(username: $user){
		username
		repos(limit: 100){
			contributors {
				username
			}
		}
	}
}

The problem - ah yes, there is one dear reader - is that defining the GraphQL server is a bunch of boilerplate:

const Post = new GraphQLObjectType({ name: "Post" , fields: () => ({ _id: {type: new GraphQLNonNull(GraphQLString)} , title: { type: new GraphQLNonNull(GraphQLString) , resolve: post => post.title || "Does not exist" } , content: {type: new GraphQLNonNull(GraphQLString)} , author: { type: Author , resolve: post => AuthorsList.filter(a => a._id === post.author).shift() } }) })

const mutation = new GraphQLObjectType({ name: "Mutations" , fields: () => ({ createPost: { type: Post , args: { title: {type: new GraphQLNonNull(GraphQLString)} , content: {type: new GraphQLNonNull(GraphQLString)} } , resolve: (source, {title, content}) => { let post = { _id: ${Date.now()}::${Math.ceil(Math.random() * 9999999)} , title , content } PostsList.push(post) return post } } }) })

// This is the Root Query const query = new GraphQLObjectType({ name: 'BlogSchema', fields: () => ({ echo: { type: GraphQLString , args: { message: { type: GraphQLString } } , resolve: (source, { message }) => Echo: ${message} } , posts: { type: new GraphQLList(Post) , resolve: () => PostsList } , authors: { type: new GraphQLList(Author) , resolve: () => AuthorsList } }) })

// The Schema const Schema = new GraphQLSchema({ query, mutation })

export default Schema

</details>

<details>
<summary>Instead of doing that, I tried my hand at creating a language to specify all that mess:</summary>
```js
import gqlSchema from './index'

const schema = `

Author {
    id: Int!
    firstName: String
    lastName: String
	votes: Int
}

Post {
    id: Int!
    title: String
    votes: Int
	author: Author
}

Query {
	authors: [Author]
    posts: [Post]
}

Mutation {
    upvotePost(postId: Int!): Post
    updateAuthor(authorId: Int!): Author
    addPost(title:String votes:Int author:Int): Post
    addAuthor(firstName:String lastName:String votes:Int): Author
}

`

let queries = [
`
mutation {
	newAuthor: addAuthor(
		firstName:"Matt"
		lastName:"K"
		votes:1
    ){
		id, firstName, lastName, votes
    }
}
`,
`
mutation {
	newPost: addPost(title:"test", votes:0, author:0){
		id,
		title,
		votes,
		author {
			id, firstName, lastName, votes
        }
	}
}
`,
`
{
	authors { id firstName lastName votes  }
	posts { id title votes }
}
`
]


let posts = []
	, authors = []

const resolvers = {
    Query: {
        posts: () => posts
        , authors: () => authors
    }
    , Mutation: {
        upvotePost: (_, {postId}) => {
            const p = posts.filter(x => postId === x.id)
            if(!p) throw `Couldn't find post with id ${postId}`
            p.votes += 1
            return p
        }
        , upvoteAuthor: (_, {authorId}) => {
            const a = authors.filter(x => authorId === x.id)
            if(!a) throw `Couldn't find author with id ${authorId}`
            a.votes++
            return a
        }
        , addPost: (_, {title, votes, author}) => {
            let p ={
                id: posts.length
                , title
                , votes
                , author
            }
            posts.push(p)
            return p
        }
        , addAuthor: (_, {firstName, lastName, votes}) => {
            let a = {
                id: authors.length
                , firstName
                , lastName
                , votes
            }
            authors.push(a)
            return a
        }
    }
    , Post: {
        author: post =>
        	authors
        	.filter(x => x.id === post.author)
        	.shift()
    }
}

let s = gqlSchema(schema, resolvers)

const log = (a) => console.log(JSON.stringify(a, null, 2))

queries.reduce((acc,q) =>
	acc.then(() => s(q).then(log))
	, Promise.resolve(true))

Hopefully this proves as useful to you as it does to me. :-)


NPM Build Status

Usage

yarn add graphdl
# or
npm install --save graphdl

Try It Out!

Check out the code from example.js:

import gqlSchema from 'graphql'

const schema = `

Author {
    id: Int!
    firstName: String
    lastName: String
	votes: Int
}

Post {
    id: Int!
    title: String
    votes: Int
	author: Author
}

Query {
	authors: [Author]
    posts: [Post]
}

Mutation {
    upvotePost(postId: Int!): Post
    updateAuthor(authorId: Int!): Author
    addPost(title:String votes:Int author:Int): Post
    addAuthor(firstName:String lastName:String votes:Int): Author
}

`

let queries = [
`
mutation {
	newAuthor: addAuthor(
		firstName:"Matt"
		lastName:"K"
		votes:1
    ){
		id, firstName, lastName, votes
    }
}
`,
`
mutation {
	newPost: addPost(title:"test", votes:0, author:0){
		id,
		title,
		votes,
		author {
			id, firstName, lastName, votes
        }
	}
}
`,
`
{
	authors { id firstName lastName votes  }
	posts { id title votes }
}
`
]


let posts = []
	, authors = []

const resolvers = {
    Query: {
        posts: () => posts
        , authors: () => authors
    }
    , Mutation: {
        upvotePost: (_, {postId}) => {
            const p = posts.filter(x => postId === x.id)
            if(!p) throw `Couldn't find post with id ${postId}`
            p.votes += 1
            return p
        }
        , upvoteAuthor: (_, {authorId}) => {
            const a = authors.filter(x => authorId === x.id)
            if(!a) throw `Couldn't find author with id ${authorId}`
            a.votes++
            return a
        }
        , addPost: (_, {title, votes, author}) => {
            let p ={
                id: posts.length
                , title
                , votes
                , author
            }
            posts.push(p)
            return p
        }
        , addAuthor: (_, {firstName, lastName, votes}) => {
            let a = {
                id: authors.length
                , firstName
                , lastName
                , votes
            }
            authors.push(a)
            return a
        }
    }
    , Post: {
        author: post =>
        	authors
        	.filter(x => x.id === post.author)
        	.shift()
    }
}

let s = gqlSchema(schema, resolvers)

const log = (a) => console.log(JSON.stringify(a, null, 2))

queries.reduce((acc,q) =>
	acc.then(() => s(q).then(log))
	, Promise.resolve(true))

Caught a bug?

  1. Fork this repository to your own GitHub account and then clone it to your local device
  2. Install the dependencies: yarn
  3. Bundle the source code and watch for changes: npm start

After that, you'll find the code in the ./build folder!

Authors

  • Matthew Keas, @matthiasak. Need help / support? Create an issue or ping on Twitter.