Alternative data-centred API alternative to REST. Declarative data fetching (fetch exactly what client want), single endpoint, GraphQL is query language for API. Single fetch (not n+1) and get exact amount of data. Detach API with front-end so front end can evolve quickly. Strong typed.
Basic parts: SDL for schema, query for fetching data, mutation for updating data (can also fetch), subscriptions for server pushing
Schema defines strong typed "type","Query","Mutation"&"Subscription". It's the contract and documentation between client & server.
To define a type:
type Person {
name: String!
age: Int!
posts: [Post!]!
}
GraphQL is strong typed. It cannot leave an field as "just any object". See https://stackoverflow.com/questions/45842544/graphql-objecttype-with-dynamic-fields-based-on-arguments. Solution would be use JSON.
Scala: String, Int, Float, Boolean, ID - possible custom scalar type (like Date, needs to define how it's validated/serialized/deserialized)
Object: such as Person & Post
Enums: enum Weekday {Mon,Tue...}
, special scala types
Interface: just a set of fields that whatever type that "implements" it, must have (not necessary but as a check mechanism)
interface Node {
id: ID!
}
type User implements Node {
id: ID!
name: String!
age: Int!
}
Union Types: a type is one of a collection of types
union Person = Adult | Child
When query
{
allPersons {
name # works for `Adult` and `Child`
... on Child {
school
}
... on Adult {
work
}
}
}
Arguments is not a standalone part of schema but used in Query, Mutation & Subscription etc. Each in definition needs a name & type.
To reuse types for arguments, create input types (just replace "type" with "input". Then use input type as type of argument.
Fetch from single API end-point which is not bound to data schema
To define:
type Query {
allPersons(last: Int): [Person!]!
}
To use:
{
allPersons(last: 2) {
name
}
}
Returns:
{
"data": {
"allPersons": [
{
"name": "Bob"
},
]
}
}
Query parts:
To define
type Mutation {
createPerson(name: String!, age: Int!): Person!
}
To use
mutation {
createPerson(name: "Bob", age: 36) {
id
}
}
"mutation":
Represents a stream of data from server through a steady connection.
To define
type Subscription {
newPerson: Person!
}
To use
subscription {
newPerson {
name
age
}
}
For organize and reuse:
fragment addressDetails on User {
name
street
zipcode
city
}
to use
{
allUsers {
... addressDetails
}
}
One request can do multiple queries & possibly causes naming issues. Alias query specify name for results:
query {
__schema {
types {
name
}
}
}
{
__type(name: "Author") {
name
description
}
}
It's a challenge admittedly.
Recommended to be implemented in OAuth fashion. Recommended not to handle in GraphQL implementation.
Returns "errors":[...] array under root (sibling to "data")
Strategies to consider:
GraphQL is only specification. Backend implementation can be:
Resolve functions:
Backend:
Front-end:
Introspection:
Play Ground: