1 of 39

MANOMANO

Native GraphQL in PHP:

A journey

2 of 39

GraphQWhat?

Introduction to GraphQL

3 of 39

GraphQWhat?

Introduction to GraphQL

  • A query language for APIs
  • Developed by Facebook in 2015
  • Auto-documented by design
  • Clients can ask precisely what they need
  • Possible to group queries/mutations in a single HTTP exchange

Learn how it works on their official page

ManoMano confidential

4 of 39

GraphQWhat?

GraphQL VS REST

  • REST is NOT inherently better or worse than GraphQL
  • There is nothing that either can do that the other cannot
  • REST is perfect when there are few possible use-cases from API clients
  • GraphQL is better when the same data can be requested at multiple places under various forms
  • GraphQL is more flexible, but this can lead to optimization/performance problems

ManoMano confidential

5 of 39

GraphQWhat?

So why choose GraphQL at ManoMano?

  • Used with consultation endpoints
    • Same data called in various ways all over the website -> flexibility helps
    • We won't need a msf anymore!

  • Administration endpoints for back-offices still use REST
    • More controlled environment
    • Less possible shapes for queries, flexibility not a requirement here

ManoMano confidential

6 of 39

GraphQWhat?

Introduction to Apollo Federation

Microservice architecture + GraphQL = Problems?

We want a single GraphQL schema for the whole website, gluing together all GraphQL APIs from our microservices

Solution => Apollo GraphQL Federation

  • Created by Apollo
  • Combines all subscribed graphs into a single super-graph
  • Gateway for GraphQL - orchestrates calls to subscribed graphs
  • Allows to add centralized cache, header, authentication, etc.
  • Currently in its v2

ManoMano confidential

7 of 39

GraphQWhat?

Introduction to Apollo Federation

ManoMano confidential

8 of 39

GraphQWhat?

Introduction to Apollo Federation

Apollo federation norms v1 requires the following to be implemented:

  • Directives:
    • @key: how to identify a federated entity
    • @external: mark a field as defined in another graph
    • @extends: mark an entity as defined in another graph, add new fields to it
    • @requires: mark a field as only able to be resolved using fields from other graphs
    • @provides: mark a field as able to be resolved by this graph under certain conditions
  • Queries:
    • query._service: runtime introspection query specific to federation
    • query._entities: fetch multiple resources by their keys
  • Types:
    • _Service: federation service description using Schema Definition Language format
    • _Entity: union of all federated entities handled by this service
    • _Any: anything - used as input for query._entities

ManoMano confidential

9 of 39

GraphQL at ManoMano

Our adoption plan

10 of 39

GraphQL at ManoMano

Adoption plan: current state

ManoMano confidential

11 of 39

GraphQL at ManoMano

Adoption plan: target state

ManoMano confidential

12 of 39

Apollo Federation in PHP

Application to our homepage microservice

13 of 39

Apollo Federation in PHP

Situation before GraphQL

ManoMano confidential

14 of 39

Apollo Federation in PHP

Situation before GraphQL

So, we want to try out GraphQL now

We will create a new "adapter" component that will convert the public REST API into a GraphQL one

The NodeJS dog

All adapters will be in NodeJS! YOLOL!!!

LOL!!!

ManoMano confidential

15 of 39

Apollo Federation in PHP

Situation during GraphQL experiment

ManoMano confidential

16 of 39

Apollo Federation in PHP

Situation during GraphQL experiment

The NodeJS dog

Le PHP dog

We can't maintain the adapter anymore. You do it. LOL.

Wait, quoi? We don't know nodeJS!

LOL?

How about we handle le GraphQL directly from le microservice?

We could maintain le GraphQL then. This removes complexity and improves performance.

We will changer some things, but we will make sure to include your legacy API

ManoMano confidential

17 of 39

Apollo Federation in PHP

Situation after adding Federation to the ms

ManoMano confidential

18 of 39

Apollo Federation in PHP

Situation after adding Federation to the ms

Le PHP dog

The archi-dog

Bien sûr! We will clean this up once we're sûrs le public REST API is no longer used.

Very nice! But your work is not completely done.

Don't forget one of our goals with GraphQL is to get rid of MSF.

This could take some time, though…

ManoMano confidential

19 of 39

Apollo Federation in PHP

Target situation

ManoMano confidential

20 of 39

Steak tack

or is it Tech Stack?

21 of 39

Tech stack

Analysis

The official Federation page of the Apollo website lists officially recognized implementations in various languages, including PHP.

Only 2 libraries implement Federation:

  • One of them is Lighthouse, compatible with Laravel but not Symfony
  • … so I had to choose the other one: skillshare/apollo-federation-php

Neither are compatible yet with Federation V2.

ManoMano confidential

22 of 39

Tech stack

Green elements are new to the stack

  • PHP 8.1
  • Symfony 6.2
  • Various ManoMano dependencies
  • skillshare/apollo-federation-php 1.6
  • webonyx/graphql 14.11

ManoMano confidential

23 of 39

How it works

24 of 39

How it works

Resulting schema

Link to the federated schema here

  • Native GraphQL format
  • Schema is declared in the repository
  • Includes the legacy API of the adapter

ManoMano confidential

25 of 39

How it works

Controller

Link to the repository here

  1. Converts the incoming request to a DTO
  2. Calls the GraphQL service
  3. Any error is printed to outgoing log systems
  4. Converted to a JSON response object
  5. If no error, cache instructions are added to the response headers

ManoMano confidential

26 of 39

How it works

Infrastructure code

Link to the repository here

Folders:

  • DTO/: All the required DTO objects (only the RequestDTO for now)
  • Exceptions/: Custom exceptions
  • Type/: All types, 1 class per GraphQL type, grouped by Enum/, Scalar/, Object/

Files:

  • Query.php: Query declaration
  • LegacyQueryDecorator.php: Holds all resolvers from the legacy API
  • Schema.php: Overall schema declaration

ManoMano confidential

27 of 39

How it works

Non-blocking errors

Link to the repository here

Problem:

  • When an exception is thrown from within a resolver, the whole request fails
  • Some errors should not prevent the whole request from failing

Solution:

  • Dedicated service NonBlockingErrorCollection passed to resolvers
  • Errors are added to the service instead of throwing exceptions
  • Rendered in the errors key in the JSON response

ManoMano confidential

28 of 39

How it works

Limited listing

Link to the repository here

For this specific API, none of the listing actions needs pagination. This does not mean we should allow any number of items to be returned!

  • Ensure the API cannot return an infinite number of items
  • Outputs limited to 1000 items everywhere
  • When limit is reached, a non-blocking error is added to the stack
  • When using pagination: ensure there is a upper limit on the items per page

ManoMano confidential

29 of 39

Caveats

30 of 39

Caveats

Federation v2

Not available yet… An MR on skillshare/apollo-federation-php is on the way but is not merged yet.

Open-source world is slow-paced -> we should not wait for this feature to be merged.

In case this cause a problem on the long-term, either

  • Work on the package to make this progress, or
  • Fork the dependency on our own GIT repository, or
  • Use the Federation v2 dev branch directly

ManoMano confidential

31 of 39

Caveats

Schema maintenance

The following is specific to ManoMano

Code First: The GraphQL schema is generated from code instead of versioned

Tools do exist in the package to generate the schema from code.

However the result is not compatible with our current supergraph deployment system. The schema generation system is not extensible and this behaviour cannot be fixed easily.

Consequently the schema must be maintained manually

ManoMano confidential

32 of 39

Caveats

Graph renaming

The following is not specific to PHP

The following is specific to ManoMano

The legacy graph was named homepage-b2c, we wanted to rename it to homepage

However the current deployment system won't allow this - because it would create service interruptions

ManoMano confidential

33 of 39

If you want to do the same thing

Some advice

34 of 39

If you want to do the same thing

Architecture

The following advice is not specific to PHP

  • Don't forget to add cache headers when relevant
    • Federation should make the synthesis of cache headers from all sub-responses
    • Test your cache directives through federation

  • Share and ask reviews on your schema before pushing it to federation
    • The community is here to help
    • Once used in PRD, any change can become hard to handle

ManoMano confidential

35 of 39

If you want to do the same thing

Development

The following advice is not specific to PHP

  • Test the federated queries, as the federation WILL call them:
    • query.__service
    • query.__entities

ManoMano confidential

36 of 39

Conclusions

37 of 39

Conclusion

Takeaways

  • Implementing GraphQL with Federation natively is possible, and not that hard
  • Some problems remain but are not blocking
  • Only works with federation v1 for now

Few ManoMano projects still use PHP, so a specific lib/bundle was not deemed necessary in our case.

ManoMano confidential

38 of 39

Resources

39 of 39

Resources

Some links

ManoMano confidential