Dilemmas and decisions
What we’ve learned designing the new Sylius API
Introduction
01
Photo by Mikhail Vasilyev on Unsplash
sylius.com
3
Certi
fi
cation 🎄
SYLIUSROCKS
-30%
Paid OS Support
Modular SyliusPlus
sylius.com
8
sylius.com
9
Started in 2020
1.12 with AP 2.7 (since 31st of Oct)
1.13 stabilized with AP 3.0 🎉
~100% of shop & ~70% of all coverage
Photo by Shane Aldendorff on Unsplash
Decisions & consequences
02
Strategic design
sylius.com
12
ADRs
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
[short title of solved problem and solution]
Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005]
Date: [YYYY-MM-DD when the decision was last updated]
Context and Problem Statement
[Describe the context and problem statement]
Decision Drivers
[driver 1, e.g., a force, facing concern, …]
…
Considered Options
[option 1]
[example | description | pointer to more information | …]
Good, because [argument a]
Bad, because [argument b]
…
Decision Outcome
Chosen option: "[option 1]", because [justi
fi
cation].
References
[Link type] [Link to ADR]
…
sylius.com
21
Conclusion?
sylius.com
22
GraphQL vs REST
2020 -> GraphQL 🚀
Is it still?
Is it not?
sylius.com
28
✔ Solves over fetching and under fetching
By design
✔ Typed, nice documentation
? Sends everything with POST
It is possible to do it with GET
✔ Gracefully deprecation of queries
Which was not possible with default REST
GraphQL
sylius.com
29
✔ May solve over fetching and under fetching
With spare
fi
elds sets and/or Vulcain
✔ Typed, nice documentation
With OpenAPI
✔ Takes advantage of 20 years of web cache development
Fake date institute ™
✔ Gracefully deprecation of queries
With OpenAPI
REST
sylius.com
30
Conclusion?
Resources design
sylius.com
32
State transitions
Case
Let’s cancel an order!
sylius.com
34
Considered option #1
PATCH /api/orders/42/
{
"state": "cancelled"
}
sylius.com
35
Considered option #2
PATCH /api/orders/42/cancel
{}
RESTful Archetypes
Document
/api/admin/orders/1
Store
Client controlled /api/admin/orders/123
Collections
Server controlled /api/admin/orders
Controller
/api/admin/orders/1/cancel
Based on: REST API Design Rulebook by Mark Masse
sylius.com
38
Isn’t there a better way?
sylius.com
39
Solution?
POST /api/orders-cancellation-requests/
{}
sylius.com
40
Conclusion?
sylius.com
41
Calculated data
Case
Cost of the shipment
Version #1 - Adding
fi
elds on entity
class ShippingMethod
{
/** rest of methods */
public ?int $cost; // never used in app
// serialized only when possible to count
}
Version #2 - Read model
class CartShippingMethod
{
public function __construct(
public readonly string $code,
public readonly ShippingMethodInterface $shippingMethod,
public readonly int $cost
) {
}
}
Version #3 - Dynamic
fi
eld
public function normalize($object, $format = null, array $context = [])
{
Assert::keyNotExists($context, self::ALREADY_CALLED);
$context[self::ALREADY_CALLED] = true;
$data = $this->normalizer->normalize($object, $format, $context);
$calculator = $this->shippingCalculators->get($object->getCalculator());
$data['price'] = $calculator->calculate(
$shipment,
$object->getConfiguration()
);
return $data;
}
public function normalize($object, $format = null, array $context = [])
{
Assert::keyNotExists($context, self::ALREADY_CALLED);
$context[self::ALREADY_CALLED] = true;
$data = $this->normalizer->normalize($object, $format, $context);
$calculator = $this->shippingCalculators->get($object->getCalculator());
$data['price'] = $calculator->calculate(
$shipment,
$object->getConfiguration()
);
return $data;
}
public function normalize($object, $format = null, array $context = [])
{
Assert::keyNotExists($context, self::ALREADY_CALLED);
$context[self::ALREADY_CALLED] = true;
$data = $this->normalizer->normalize($object, $format, $context);
$calculator = $this->shippingCalculators->get($object->getCalculator());
$data['price'] = $calculator->calculate(
$shipment,
$object->getConfiguration()
);
return $data;
}
public function normalize($object, $format = null, array $context = [])
{
Assert::keyNotExists($context, self::ALREADY_CALLED);
$context[self::ALREADY_CALLED] = true;
$data = $this->normalizer->normalize($object, $format, $context);
$calculator = $this->shippingCalculators->get($object->getCalculator());
$data['price'] = $calculator->calculate(
$shipment,
$object->getConfiguration()
);
return $data;
}
Version #3 - Dynamic
fi
eld
sylius.com
47
Conclusion?
High level API design
sylius.com
49
Uni
fi
cation of API
/api/products/
Admin & Shop served together
sylius.com
51
✘ Available
fi
elds
Complicated serialisation groups depending on logged in user
✘ Hard to de
fi
ne identi
fi
ers
We have resigned from them later
✘ Requirement to de
fi
ne granular access control
To now allow to access sensitive date for non-admins
Findings
sylius.com
52
🛒 Shop has 72 endpoints
64% of read endpoints
🖊 Only 20% of resources have writable capabilities in shop
40% of them are never exposed in shop
⚙ Admin has 128 endpoints
52% of read endpoints
Data
Option #1 - Admin & Shop pre
fi
xed
/api/products/?admin
sylius.com
54
✔ Available
fi
elds
Depending on logged in user
✘ Seems wrong from the REST perspective
If we add pre
fi
x to the URL
✘ Requirement to de
fi
ne granular access control
To now allow to access sensitive date for non-admins
Findings
Option #2 - Admin & Shop header split
/api/products/
Accept:
application/vnd.sylius-admin.api+json
sylius.com
56
✔ Available
fi
elds
Depending on logged in user
✔ REST compilant
✘ Requirement to de
fi
ne granular access control
May be mitigated with Voters
Findings
✘ Not easily supported
By API Platform and Open API spec
Option #3 - Admin & Shop pre
fi
xed
/api/shop/products/
/api/admin/products/
sylius.com
58
✔ Available
fi
elds
Depending on logged in user
✔/✘ REST compilant
Disputable
✔ Straightforward access control
Just with security con
fi
g
Findings
✔ Easily supported
By API Platform and Open API spec
sylius.com
59
Conclusion?
sylius.com
60
API versioning
/api/v1
Old Admin API
#1 version
/new-api/
sylius.com
63
URL based versioning
/api/v2
Custom header
X-Sylius-API-Version: 1
Accept header with an additional vendor information
Accept: application/vnd.sylius.v1+json
#2 version
sylius.com
66
Conclusion?
sylius.com
67
But!
sylius.com
68
🔮 Versioning endpoints
With sunset header
🔮 Vendor added to accept header
application/vnd.sylius.v1+json
🔮 Deprecating endpoints,
fi
elds etc
In documentation of Open API
Future
API
fl
ow design
Case #1
Add to cart
Simple product
{
"product": “/api/products/42“,
"quantity": 1
}
Con
fi
gurable product #1
{
"product": “/api/products/42“,
"productVariant": “/api/product-variants/42“,
"quantity": 1
}
{
"product": “/api/products/42“,
“options": {
"SIZE": “SIZE_L",
"COLOR": “COLOR_BLUE"
},
"quantity": 1
}
Con
fi
gurable product #2
{
"product": “/api/products/864“,
"productVariant": “/api/product-variants/864“,
"quantity": 1
}
Let’s improve!
{
"product": “/api/products/42“,
"quantity": 1
}
Configurable
product
Simple
product
Let’s improve!
{
"product": “/api/products/864“,
"productVariant": “/api/product-variants/864“,
"quantity": 1
}
{
"product": “/api/products/42“,
"productVariant": “/api/product-variants/42“,
"quantity": 1
}
Configurable
product
Simple
product
Let’s improve!
{
"product": “/api/products/42“,
"productVariant": “/api/product-variants/42“,
"quantity": 1
} Configurable
product
Simple
product
Let’s improve!
{
"productVariant": “/api/product-variants/42“,
"quantity": 1
}
Configurable
product
Simple
product
sylius.com
78
But what with options?
Price matrix in UI
or
Ask us
sylius.com
80
Conclusion?
Case #2
Order details
Apply coupon
Apply coupon
PATCH
 ​​
/ap
i​
/v
2​
/sho
p​
/order
s​
/TOKEN_VALU
E​
/apply-coupon
{
"couponCode": “CHRISTMAS_SALE"
}
Cart claiming & addressing
Cart claiming & addressing
PATCH
 ​
/ap
i​
/v
2​
/sho
p​
/order
s​
/TOKEN_VALUE/address
{
"email": “test@example.com”,
"billingAddress": {
"firstName": "Jane",
"lastName": "Doe",
"...": "..."
}
}
sylius.com
86
✔ We are used to this separation
Mockups “force” such design
✔ Addressing requires state machine transition
While coupon appliance cart processing
✔ Di
ff
erent data required on di
ff
erent pages
Reasoning?
What about order update?
Order update
PUT
 ​
/ap
i​
/v
2​
/sho
p​
/order
s​
/TOKEN_VALUE
{
"localeCode": “en_US”
}
But it is just order drafting!
sylius.com
90
✔ UI Mockups should not force any design decision
We can preload data from more then one endpoint
✔ Don’t use state machine where there is none
✔ Either store data earlier or use partial update
Changed attitude
PUT
 ​
/ap
i​
/v
2​
/sho
p​
/order
s​
/TOKEN_VALUE/
{
"email": “test@example.com”,
"billingAddress": {
"firstName": "Jane",
"lastName": "Doe",
"countryCode": "US",
"street": “Baker Street 221B”,
"city": “London",
"postcode": "Doe"
},
"couponCode": “CHRISTMAS_SALE"
}
Order update
sylius.com
92
Conclusion?
Photo by Mikhail Vasilyev on Unsplash
03
Takeaways
sylius.com
94
Use ADRs
And browse them from time to time
Custom logic? New API resource!
Let’s behave like a tax department!
REST will be with us for the long time
But GraphQL will be there as well
Do not map HTML based websites to your API
I know, it was obvious 😅
sylius.com
95
Thank you!
Photo by Anthony DELANOIX on Unsplash
@lukaszchrusciel
@lchrusciel@mastodon.social
@lchrusciel

SymfonyCon - Dilemmas and decisions..pdf

  • 1.
    Dilemmas and decisions Whatwe’ve learned designing the new Sylius API
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
    sylius.com 9 Started in 2020 1.12with AP 2.7 (since 31st of Oct) 1.13 stabilized with AP 3.0 🎉 ~100% of shop & ~70% of all coverage
  • 10.
    Photo by ShaneAldendorff on Unsplash Decisions & consequences 02
  • 11.
  • 12.
  • 13.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 14.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 15.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 16.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 17.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 18.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 19.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 20.
    [short title ofsolved problem and solution] Status: [proposed | rejected | accepted | deprecated | … | superseded by ADR-0005] Date: [YYYY-MM-DD when the decision was last updated] Context and Problem Statement [Describe the context and problem statement] Decision Drivers [driver 1, e.g., a force, facing concern, …] … Considered Options [option 1] [example | description | pointer to more information | …] Good, because [argument a] Bad, because [argument b] … Decision Outcome Chosen option: "[option 1]", because [justi fi cation]. References [Link type] [Link to ADR] …
  • 21.
  • 22.
  • 23.
  • 24.
  • 27.
  • 28.
    sylius.com 28 ✔ Solves overfetching and under fetching By design ✔ Typed, nice documentation ? Sends everything with POST It is possible to do it with GET ✔ Gracefully deprecation of queries Which was not possible with default REST GraphQL
  • 29.
    sylius.com 29 ✔ May solveover fetching and under fetching With spare fi elds sets and/or Vulcain ✔ Typed, nice documentation With OpenAPI ✔ Takes advantage of 20 years of web cache development Fake date institute ™ ✔ Gracefully deprecation of queries With OpenAPI REST
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
    sylius.com 34 Considered option #1 PATCH/api/orders/42/ { "state": "cancelled" }
  • 35.
  • 37.
    RESTful Archetypes Document /api/admin/orders/1 Store Client controlled/api/admin/orders/123 Collections Server controlled /api/admin/orders Controller /api/admin/orders/1/cancel Based on: REST API Design Rulebook by Mark Masse
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
    Version #1 -Adding fi elds on entity class ShippingMethod { /** rest of methods */ public ?int $cost; // never used in app // serialized only when possible to count }
  • 44.
    Version #2 -Read model class CartShippingMethod { public function __construct( public readonly string $code, public readonly ShippingMethodInterface $shippingMethod, public readonly int $cost ) { } }
  • 45.
    Version #3 -Dynamic fi eld
  • 46.
    public function normalize($object,$format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } public function normalize($object, $format = null, array $context = []) { Assert::keyNotExists($context, self::ALREADY_CALLED); $context[self::ALREADY_CALLED] = true; $data = $this->normalizer->normalize($object, $format, $context); $calculator = $this->shippingCalculators->get($object->getCalculator()); $data['price'] = $calculator->calculate( $shipment, $object->getConfiguration() ); return $data; } Version #3 - Dynamic fi eld
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
    sylius.com 51 ✘ Available fi elds Complicated serialisationgroups depending on logged in user ✘ Hard to de fi ne identi fi ers We have resigned from them later ✘ Requirement to de fi ne granular access control To now allow to access sensitive date for non-admins Findings
  • 52.
    sylius.com 52 🛒 Shop has72 endpoints 64% of read endpoints 🖊 Only 20% of resources have writable capabilities in shop 40% of them are never exposed in shop ⚙ Admin has 128 endpoints 52% of read endpoints Data
  • 53.
    Option #1 -Admin & Shop pre fi xed /api/products/?admin
  • 54.
    sylius.com 54 ✔ Available fi elds Depending onlogged in user ✘ Seems wrong from the REST perspective If we add pre fi x to the URL ✘ Requirement to de fi ne granular access control To now allow to access sensitive date for non-admins Findings
  • 55.
    Option #2 -Admin & Shop header split /api/products/ Accept: application/vnd.sylius-admin.api+json
  • 56.
    sylius.com 56 ✔ Available fi elds Depending onlogged in user ✔ REST compilant ✘ Requirement to de fi ne granular access control May be mitigated with Voters Findings ✘ Not easily supported By API Platform and Open API spec
  • 57.
    Option #3 -Admin & Shop pre fi xed /api/shop/products/ /api/admin/products/
  • 58.
    sylius.com 58 ✔ Available fi elds Depending onlogged in user ✔/✘ REST compilant Disputable ✔ Straightforward access control Just with security con fi g Findings ✔ Easily supported By API Platform and Open API spec
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
    sylius.com 63 URL based versioning /api/v2 Customheader X-Sylius-API-Version: 1 Accept header with an additional vendor information Accept: application/vnd.sylius.v1+json #2 version
  • 66.
  • 67.
  • 68.
    sylius.com 68 🔮 Versioning endpoints Withsunset header 🔮 Vendor added to accept header application/vnd.sylius.v1+json 🔮 Deprecating endpoints, fi elds etc In documentation of Open API Future
  • 69.
  • 70.
  • 71.
  • 72.
    Con fi gurable product #1 { "product":“/api/products/42“, "productVariant": “/api/product-variants/42“, "quantity": 1 }
  • 73.
    { "product": “/api/products/42“, “options": { "SIZE":“SIZE_L", "COLOR": “COLOR_BLUE" }, "quantity": 1 } Con fi gurable product #2
  • 74.
    { "product": “/api/products/864“, "productVariant": “/api/product-variants/864“, "quantity":1 } Let’s improve! { "product": “/api/products/42“, "quantity": 1 } Configurable product Simple product
  • 75.
    Let’s improve! { "product": “/api/products/864“, "productVariant":“/api/product-variants/864“, "quantity": 1 } { "product": “/api/products/42“, "productVariant": “/api/product-variants/42“, "quantity": 1 } Configurable product Simple product
  • 76.
    Let’s improve! { "product": “/api/products/42“, "productVariant":“/api/product-variants/42“, "quantity": 1 } Configurable product Simple product
  • 77.
  • 78.
  • 79.
    Price matrix inUI or Ask us
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
    Cart claiming &addressing
  • 85.
    Cart claiming &addressing PATCH ​ /ap i​ /v 2​ /sho p​ /order s​ /TOKEN_VALUE/address { "email": “test@example.com”, "billingAddress": { "firstName": "Jane", "lastName": "Doe", "...": "..." } }
  • 86.
    sylius.com 86 ✔ We areused to this separation Mockups “force” such design ✔ Addressing requires state machine transition While coupon appliance cart processing ✔ Di ff erent data required on di ff erent pages Reasoning?
  • 87.
  • 88.
  • 89.
    But it isjust order drafting!
  • 90.
    sylius.com 90 ✔ UI Mockupsshould not force any design decision We can preload data from more then one endpoint ✔ Don’t use state machine where there is none ✔ Either store data earlier or use partial update Changed attitude
  • 91.
    PUT ​ /ap i​ /v 2​ /sho p​ /order s​ /TOKEN_VALUE/ { "email": “test@example.com”, "billingAddress":{ "firstName": "Jane", "lastName": "Doe", "countryCode": "US", "street": “Baker Street 221B”, "city": “London", "postcode": "Doe" }, "couponCode": “CHRISTMAS_SALE" } Order update
  • 92.
  • 93.
    Photo by MikhailVasilyev on Unsplash 03 Takeaways
  • 94.
    sylius.com 94 Use ADRs And browsethem from time to time Custom logic? New API resource! Let’s behave like a tax department! REST will be with us for the long time But GraphQL will be there as well Do not map HTML based websites to your API I know, it was obvious 😅
  • 95.
    sylius.com 95 Thank you! Photo byAnthony DELANOIX on Unsplash @lukaszchrusciel @lchrusciel@mastodon.social @lchrusciel