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 | ChildWhen 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: