每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;微博:https://weibo.com/openingsource;电报群 https://t.me/OpeningSourceOrg


今日推荐开源项目:《你说人话我也听得懂 NLP-progress》传送门:GitHub链接

推荐理由:NLP,全称自然语言处理,是人工智能里面关于让机器听懂人话的那个方面。这个项目就记录了当前 NLP 的发展程度,比如词性标注,阅读理解和推理等等。如果你对如何让计算机读懂人话感兴趣的话,不妨来这里看一看。


今日推荐英文原文:《An Introduction to GraphQL》作者:Flavio Copes

原文链接:https://hackernoon.com/an-introduction-to-graphql-2c3f7d8fb4e0

推荐理由:介绍了 GraphQL,最近工场内也有简单介绍 GraphQL 的文章,想看看的话可以点击这个链接(传送门)。

An Introduction to GraphQL

GraphQL is the new frontier in APIs (Application Programming Interfaces).

It’s a query language for your API, and a set of server-side runtimes (implemented in various backend languages) for executing queries.

It’s not tied to a specific technology, but you can implement it in any language.

It is a methodology that directly competes with REST (Representational state transfer) APIs, much like REST competed with SOAP at first.

GraphQL was developed at Facebook, like many of the technologies that are shaking the world lately, like React and React Native, and it was publicly launched in 2015 — although Facebook used it internally for a few years before.

Many big companies are adopting GraphQL beside Facebook, including GitHub, Pinterest, Twitter, Sky, The New York Times, Shopify, Yelp and thousands many other.

GraphQL Principles

GraphQL exposes a single endpoint.

You send a query to that endpoint by using a special Query Language syntax. That query is just a string.

The server responds to a query by providing a JSON object.

Let’s see a first example of such a query. This query gets the name of a person with id=1:

GET /graphql?query={ person(id: "1") { name } }

or simply

{ person(id: "1") { name } }

We’ll get this JSON response back:

{ "name": "Tony" }

Let’s add a bit more complexity: we get the name of the person, and the city where the person lives, by extracting it from the address object. We don’t care about other details of the address, and the server does not return them back to us.

GET /graphql?query={ person(id: "1") { name, address { city } } }

or

{ 
  person(id: "1") { 
    name 
    address { 
      city 
    } 
  } 
}
{ 
  "name": "Tony", 
  "address": { 
    "city": "York" 
  } 
}

As you can see the data we get is basically the same request we sent, filled with values.

GraphQL vs REST

Since REST is such a popular, or I can say universal, approach to building APIs, it’s fair to assume you are familiar with it, so let’s see the differences between GraphQL and REST.

Rest is a concept

REST is a de-facto architecture standard but it actually has no specification and tons of unofficial definitions. GraphQL has a specification draft, and it’s a Query Language instead of an architecture, with a well defined set of tools built around it (and a flourishing ecosystem).

While REST is built on top of an existing architecture, which in the most common scenarios is HTTP, GraphQL is building its own set of conventions. Which can be an advantage point or not, since REST benefits for free by caching on the HTTP layer.

A single endpoint

GraphQL has only one endpoint, where you send all your queries. With a REST approach, you create multiple endpoints and use HTTP verbs to distinguish read actions (GET) and write actions (POST, PUT, DELETE). GraphQL does not use HTTP verbs to determine the request type.

Tailored to your needs

With REST, you generally cannot choose what the server returns back to you, unless the server implements partial responses using sparse fieldsets, and clients use that feature. The API maintainer cannot enforce such filtering.

The API will usually return you much more information than what you need, unless you control the API server as well, and you tailor your responses for each different request.

With GraphQL you explicitly request just the information you need, you don’t “opt out” from the full response default, but it’s mandatory to pick the fields you want.

This helps saving resources on the server, since you most probably need less processing, and also network savings, since the payload to transfer is smaller.

GraphQL makes it easy to monitor for fields usage

With REST, unless forcing sparse fieldsets, there is no way to determine if a field is used by clients, so when it comes to refactoring or deprecating, it’s impossible to determine actual usage.

GraphQL makes it possible to track which fields are used by clients.

Access nested data resources

GraphQL allows to generate a lot less network calls.

Let’s do an example: you need to access the names of the friends of a person. If your REST API exposes a /person endpoint, which returns a person object with a list of friends, you generally first get the person information by doing GET /person/1, which contains a list of ID of its friends.

Unless the list of friends of a person already contains the friend name, with 100 friends you’d need to do 101 HTTP requests to the /person endpoint, which is a huge time cost, and also a resource intensive operation.

With GraphQL, you need only one request, which asks for the names of the friends of a person.

Types

A REST API is based on JSON which cannot provide type control. GraphQL has a Type System.

Which one is better?

Organizations around the world are questioning their API technology choices and they are trying to find out if migrating from REST to GraphQL is best for their needs.

GraphQL is a perfect fit when you need to expose complex data representations, and when clients might need only a subset of the data, or they regularly perform nested queries to get the data they need.

As with programming languages, there is no single winner, it all depends on your needs.

GraphQL Queries

In this article you’ll learn how is a GraphQL query composed.

The concepts I’ll introduce are

  • fields and arguments
  • aliases
  • fragments

Fields and arguments

Take this simple GraphQL query:

{ 
  person(id: "1") { 
    name 
  } 
}

In this query you see 2 fields, and 1 argument.

The field person returns an Object which has another field in it, a String.

The argument allows us to specify which person we want to reference. We pass an id, but we could as well pass a name argument, if the API we talk to has the option to find a person by name.

Arguments are not limited to any particular field, we could have a friends field in person that lists the friends of that person, and it could have a limit argument, to specify how many we want the API to return:

{ 
  person(id: "1") { 
    name
    friends(limit: 100) 
  } 
}

Aliases

You can ask the API to return a field with a different name, for example:

{ 
  owner: person(id: "1") { 
    fullname: name 
  } 
}

will return

{ 
  "data": {
    "owner": { 
      "fullname": "Tony" 
    } 
  } 
}

This feature, beside creating more ad-hoc naming for your client code, is the only thing that can make the query work if you need to reference the same endpoint 2 times in the same query:

{ 
  owner: person(id: "1") { 
    fullname: name 
  } 
  first_employee: person(id: "2") { 
    fullname: name 
  } 
}

Fragments

In the above query we replicated the person structure. Fragments allow us to specify the structure once (much useful with many fields):

{ 
  owner: person(id: "1") {
    ...personFields 
  } 
  first_employee: person(id: "2") {
    ...personFields
  }
} 
fragment personFields on person {
  fullname: name 
}

GraphQL Variables

More complex GraphQL queries need to use variables, a way to dynamically specify a value that is used inside a query.

In this case we added the person id as a string inside the query:

{ 
  owner: person(id: "1") { 
    fullname: name
  } 
}

The id will most probably change dynamically in our program, so we need a way to pass it, and not with string interpolation.

With variables, the same query can be written as

query GetOwner($id: String) { 
  owner: person(id: $id) { 
    fullname: name 
  } 
} 
{ 
  "id": "1"
}

In this snippet we have assigned the GetOwner name to our query. Think of it as named functions, while before you had an anonymous function. Named queries are useful when you have lots of queries in your application.

The query definition with the variables looks like a function definition, and it works in an equal way.

Making variables required

Appending a ! to the type:

query GetOwner($id: String!)

instead of $id: String will make the $id variable required.

Specifying a default value for a variable

You can specify a default value using this syntax:

query GetOwner($id: String = "1")

GraphQL Directives

Directives let you include or exclude a field if a variable is true or false.

query GetPerson($id: String, $getAddress: Boolean) { 
  person(id: $id) { 
    fullname: name, 
    address: @include(if: $getAddress) { 
      city 
      street 
      country 
    } 
  } 
}
{ 
  "id": "1", 
  "getAddress": false
}

In this case if getAddress variable we pass is true, we also get the address field, otherwise not.

We have 2 directives available: include, which we have just seen (includes if true), and skip, which is the opposite (skips if true)

@include(if: Boolean)

query GetPerson($id: String, $getAddress: Boolean) { 
  person(id: $id) { 
    fullname: name, 
    address: @include(if: $getAddress) { 
      city 
      street 
      country 
    } 
  } 
} 
{
  "id": "1", 
  "getAddress": false 
}

@skip(if: Boolean)

query GetPerson($id: String, $excludeAddress: Boolean) { 
  person(id: $id) { 
    fullname: name, 
    address: @skip(if: $excludeAddress) { 
      city 
      street 
      country 
    } 
  } 
}
{ 
  "id": "1", 
  "excludeAddress": false 
}

每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,欢迎关注开源日报。交流QQ群:202790710;微博:https://weibo.com/openingsource;电报群 https://t.me/OpeningSourceOrg