GraphQL / Apollo Federation 🚀

Created by Facebook in 2012

 

Open sourced in 2015

 

 

Created by Facebook in 2012

  

Open sourced in 201

GraphQL is a query language for your API


GraphQL is a PATTERN not a technology


GraphQL doesn't store data


GraphQL is a replacement of REST



Created by Facebook in 2012

  

Open sourced in 201

 

GraphQL is a replacement of REST

 

 

 

Let's take an example

Instarwarsgram

 

Let's take an example

Instarwarsgram
{
  "data": {
    "person": {
      "name": "Darth Vader",
      "birthYear": "41.9BBY",
      "planet": {
        "name": "Tatooine"
      },
      "movies": [
        { "title": "A New Hope" },
        { "title": "The Empire Strikes Back" },
        { "title": "Return of the Jedi" },
        { "title": "Revenge of the Sith" }
      ]
    }
  }
}
Instarwarsgram

People

Movies

Planet

GET - /person/42
GET - /movies/{id}
GET - /planets/{id}
{
  "data": {
    "person": {
      "name": "Darth Vader",
      "birthYear": "41.9BBY",
      "planetId": 53,
      "movieIds": [1, 2, 3, 6],
      "saberColor"..
    }
  }
}
Instarwarsgram

People

Movies

Planet

GET - /person/42
GET - /movies/1
GET - /movies/2
GET - /movies/3
GET - /movies/5
GET - /movies/6
GET - /planets/53
{
  "data": {
    "person": {
      "name": "Darth Vader",
      "birthYear": "41.9BBY",
      "planetId": 53,
      "movieIds": [1, 2, 3, 6],
      "saberColor"..
    }
  }
}
{
  "data": {
    "planet": {
      "name": "Tatooine",
      "positionX": "234.323",
      "positionY...
     },
  }
}
{
  "data": {
    "movie": {
      "title": "A New Hope",
     },
  }
}
{
  "data": {
    "movie": {
      "title": "A New Hope",
     },
  }
}
{
  "data": {
    "movie": {
      "title": "A New Hope",
     },
  }
}
{
  "data": {
    "movie": {
      "id": 6
      "title": "A New Hope",
      "date": "19/10/1983",
      "personIds"...
     },
  }
}

Pros and Cons?

Rest "Tailor Made"
Instarwarsgram

People

Movies

Planet

GET - /person/{id}
GET - /movies/{id}
GET - /planets/{id}
Rest "Tailor made"

People

Movies

Planet

Profile

GET - /profile/{id}
{
  "data": {
    "person": {
      "name": "Darth Vader",
      "birthYear": "41.9BBY",
      "planet": {
        "name": "Tatooine"
      },
      "movies": [
        { "title": "A New Hope" },
        { "title": "The Empire Strikes Back" },
        { "title": "Return of the Jedi" },
        { "title": "Revenge of the Sith" }
      ]
    }
  }
}

Pros and Cons?

GraphQL

GraphQL is WYSIWYG

query {

}

GraphQL is WYSIWYG

query {
   person(id: 42)
}

GraphQL is WYSIWYG

query {
   person(id: 42) {
     name
     birthYear
   }
}

GraphQL is WYSIWYG

query {
   person(id: 42) {
     name
     birthYear
     planet {
       name
     }
     movies {
       title
     }
   }
}

GraphQL is WYSIWYG

query {
   person(id: 42) {
     name
     birthYear
     planet {
       name
     }
     movies {
       title
     }
   }
}
{
  "person": {
    "name": "Darth Vader",
    "birthYear": "41.9BBY",
    "planet": {
      "name": "Tatooine"
    },
    "movies": [
      { "title": "A New Hope" },
      { "title": "The Empire Strikes Back" },
      { "title": "Return of the Jedi" },
      { "title": "Revenge of the Sith" }
    ]
  }
}

Mmh in Vscode maybe?

query {
   person(id: 42) {
     name
     birthYear
     planet {
       name
     }
     movies {
       title
     }
   }
}
type Query {
  person(id: ID!): Person
}

type Mutation {
  destroyPlanet(planet: ID!): Boolean
}

type Person {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
  planet: Planet!
  movies: [Movies]
}

type Planet {
  id: ID!
  name: String!
  positionX: Int!
  positionY: Int!
}


type Movie {
  id: ID!
  title: String!
  date: DateTime
  actors: [Person]
}

Front

const resolvers = {
  Query: {
	person: (_, args) => {
      return PersonService.find(args.id);
    }
  },
  Mutation: {},
  Person: {},
  Planet: {},
  Movie: {},
}

Back

Well .. my app is getting bigger, I want to split it in microservices...
Monolith

People

Movies

Planet

http://sw.com
Microservices

People

Movies

Planet

http://people.com
http://movie.com
http://planets.com
Rest "Tailor made" (Monolith)

 

People

Movies

Planet

Profile

GET - /profile/{id}

Text

Rest "Tailor made" (Microservices)

People

Movies

Planet

Profile

GET - /profile/{id}
Graphql

People

Movies

Planet

Profile

type Planet {
  id: ID!
  name: String!
  positionX: Int!
  positionY: Int!
}
type Movie {
  id: ID!
  title: String!
  date: DateTime
  actors: [Person]
}
type Person {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
  planet: Planet!
  movies: [Movies]
}
type Query {
  person(id: ID!): Person
}

People

Movies

Planet

Apollo Federation

Graphql
type Planet {
  id: ID!
  name: String!
  positionX: Int!
  positionY: Int!
}
type Movie {
  id: ID!
  title: String!
  date: DateTime
  actors: [Person]
}
type Person {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
  planet: Planet!
  movies: [Movies]
}
type Query {
  person(id: ID!): Person
}

Apollo Federation... How it works?

import { buildFederatedSchema } from '@apollo/federation';
import { ApolloServer, gql } from 'apollo-server';

const peopleServer = new ApolloServer({
  schema: buildFederatedSchema({ typeDefs, resolvers})
});

peopleServer.listen(5001)

People

 

import { ApolloGateway } from '@apollo/gateway';

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'people', url: 'http://localhost:5001' },
    { name: 'movies', url: 'http://localhost:5002' },
    { name: 'planet', url: 'http://localhost:5003' },
  ],
});

const gatewayServer = new ApolloServer({
  gateway,
  subscriptions: false,
});
type Person @key(fields: "id") {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
}
type Person @key(fields: "name")  @key(fields: "birthYear") {
  name: String!
  birthYear: Int!
  picture: String!
}

 @key directive defines the entity's primary key.

Entity is an object type that you define in one implementing service and can then reference and extend in other implementing services.

Entity

type Person @key(fields: id) {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
  planet: Planet!
}

extend type Planet @key(fields: id) {
	id: ID! @external
}

After you define an entity in one implementing service, other implementing services can then reference that entity. 

type Planet {
  id: ID!
  name: String!
}

People microservices

Planet microservices

Referencing

type Person @key(fields: id) {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
  planet: Planet!
}

exten type Planet @key(fields: id) {
	id: ID! @external
}
type Planet {
  id: ID!
  name: String!
}
{
  People: {
    planet(people) {
      return { 
      	__typename: "Planet", 
        id: people.planet_id 
      };
    }
  }
}
{
  Planet: {
    __resolveReference(reference) {
      return PlanetService.findOne(reference.id);
    }
  }
}

Apollo Federation

type Person @key(fields: id) {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
}
type Planet {
  id: ID!
  name: String!
}


extend type People @key(fields: id) {
	id: ID! @external
    planet: Planet
}

People microservices

Planet microservices

Extending

{
  People: {
    planet(reference) {
      return reference.planet;
    }
  }
}

Apollo Federation

type Person @key(fields: id) {
  id: ID!
  name: String!
  birthYear: Int!
  picture: String!
}
type Planet {
  id: ID!
  name: String!
}


extend type People @key(fields: id) {
	id: ID! @external
    planet: Planet
}
type Saber {
  id: ID!
  color: String!
}


extend type People @key(fields: id) {
	id: ID! @external
    size: String @external
    weight: String @external
    birthYear: String @external
    saber: String @requires(fields: "size weight birthYear")
}

Extending an entity with computed fields

Learning to scale. With GraphQL

By Luc MOMAL

Learning to scale. With GraphQL

  • 133