Comment on a payé notre dette technique avec GraphQL

Luc Momal
Staff Engineer @

Luc Momal
Staff Engineer @









The birth of legacy code.
















Entities, such as workers, companies, contracts, invoices ..















Our story starts in 2015

Some years
later





























I don't like spaghetti.. bye bye !










































Let's reconstruct
everything! 🥳
I don't like spaghetti.. bye bye !











































6 months
1 year later


BIG BANG!!!!!









































Synchronization!!


BIG BANG!!!!!

















































BIG BANG!!!!!

















































BIG BANG!!!!!















































GraphQL to the rescue.







GraphQL
GraphQL is a PATTERN not a technology
GraphQL is a query language for your API
GraphQL is a replacement for REST







GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}

query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}







GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}

query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}

{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}







GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}

query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}

{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}






GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}
query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}
{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}
const resolvers = {
Query: {
worker: (parent, args, ctx) => db.worker.get(args.id)
},
Worker: {
id: (parent) => parent.id,
firstName: (parent) => parent.firstName,
lastName: (parent) => parent.lastName,
contracts: (parent) => db.contracts.getFromWorker(parent.id)
},
Contract: {
from: (parent) => parent.from,
to: (parent) => parent.to,
company: (parent) => db.company.get(parent.companyId),
},
Company: {
name: (parent) => parent.name
}
}








GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}
query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}
{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}
const resolvers = {
Query: {
worker: (parent, args, ctx) => db.worker.get(args.id)
},
Worker: {
id: (parent) => parent.id,
firstName: (parent) => parent.firstName,
lastName: (parent) => parent.lastName,
contracts: (parent) => db.contract.getFromWorker(parent.id)
},
Contract: {
from: (parent) => parent.from,
to: (parent) => parent.to,
company: (parent) => db.company.get(parent.companyId),
},
Company: {
name: (parent) => parent.name
}
}








GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}
query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}
{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}
const resolvers = {
Query: {
worker: (parent, args, ctx) => db.worker.get(args.id)
},
Worker: {
id: (parent) => parent.id,
firstName: (parent) => parent.firstName,
lastName: (parent) => parent.lastName,
contracts: (parent) => db.contracts.getFromWorker(parent.id)
},
Contract: {
from: (parent) => parent.from,
to: (parent) => parent.to,
company: (parent) => db.company.get(parent.companyId),
},
Company: {
name: (parent) => parent.name
}
}







GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}
query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}
{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}
const resolvers = {
Query: {
worker: (parent, args, ctx) => db.worker.get(args.id)
},
Worker: {
id: (parent) => parent.id,
firstName: (parent) => parent.firstName,
lastName: (parent) => parent.lastName,
contracts: (parent) => db.contracts.getFromWorker(parent.id)
},
Contract: {
from: (parent) => parent.from,
to: (parent) => parent.to,
company: (parent) => db.company.get(parent.companyId),
},
Company: {
name: (parent) => parent.name
}
}

AMAZING!







GraphQL
type Query {
worker(id: ID!): Worker
}
type Worker {
id: ID!
firstName: String
lastName: String
contracts: [Contract]
}
type Contract {
from: Date
to: Date
company: Company
}
type Company {
name: String
}
query {
worker(id: 42) {
id
firstName
contracts {
company {
name
}
}
}
}
{
worker: {
id: 42,
firstName: "John",
contracts: [
{
company: {
name: "cie"
}
}
]
}
}
const resolvers = {
Query: {
worker: (parent, args, ctx) => db.worker.get(args.id)
},
Worker: {
id: (parent) => parent.id,
firstName: (parent) => parent.firstName,
lastName: (parent) => parent.lastName,
contracts: (parent) => db.contracts.getFromWorker(parent.id)
},
Contract: {
from: (parent) => parent.from,
to: (parent) => parent.to,
company: (parent) => db.company.get(parent.companyId),
},
Company: {
name: (parent) => parent.name
}
}

AMAZING!











































Mmh, no!!!!








It's time to pay our debt












































Mmh, no!!!!


















Migration of our last v1 feature.

GET /workers/42/contracts
{
worker: {
id: 42,
firstName: "John",
lastName: "Doe",
birthDate: "31/08/1989",
//etc...
contracts: [
{
company: {
name: "cie",
invoiceSettings: {},
contacts: {},
//etc...
}
}
]
}
}


❌ Mmh, no!!!!







read only





















GraphQL to create a new interface









REST call
read only







Data model improvement








read only



type Mission {
from: String
to: String
contracts: [Contract]
company: Company
worker: Worker
}
type Contract {
type: String
from: String
to: String
}






GraphQL to migrate
{
"company_name": "ABC Corp",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345"
},
"invoicing_settings": {
"currency": "USD",
"tax_rate": 0.08,
"invoice_terms": "Payment due within 30 days",
"invoice_footer": "Thank you for your business!"
},
"jobs": [...],
"contacts": [...],
...
}
All information related to a company is stored
in a single location in v1.
All information related to a company is stored
in a single location in v1.




GraphQL to migrate
const Company = {
"company_name": "ABC Corp",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345"
},
}
const CompanyInvoicingSettings = {
"invoicing_settings": {
"currency": "USD",
"tax_rate": 0.08,
"invoice_terms": "Payment due within 30 days",
"invoice_footer": "Thank you for your business!"
},
}
const CompanyJobs = {
"jobs": [...],
}
const CompanyContacts = {
"contacts": [...],
}
These data can be easily split into several parts.





GraphQL to migrate
type Company {
name: String
street: String
city: String
state: String
zip: String
invoicing: CompanyInvoicingSettings
job: CompanyJob
contacts: CompanyContacts
}
type CompanyInvoicingSettings {
currency: String
tax_rate: String
invoice_terms: String
invoice_footer: String
}
type CompanyJob {
title: String
location: String
}
type CompanyContacts {
name: String
email: String
phone: String
}
And we can easily create
a graphql schema
for those data




GraphQL to migrate




















GraphQL to migrate
























With Federation
you can merge
your graph! 🚀




GraphQL to migrate
























With Federation
you can merge
your graph! 🚀




Thank you for listening

How we have paid our debt with GraphQL
By Luc MOMAL
How we have paid our debt with GraphQL
- 471