Advanced GraphQL API Usage in Crystallize
Psst: we are hiring remote frontend developers and backend developers.
Before we start - all the endpoints can be found in the GraphQL playground mentioned below.
Since Crystallize began, has been chosen as a primary query language for all endpoints. The declarative nature of the language allows for fetching just the data we need, making all requests very predictable, minimizing the transferred data, and improving the response time. efficiently manage core shopping functionalities like product catalogs, cart management, checkout processes, and orders.
GraphQL Playground
The best place to test a query or check a schema is the . The playground is a user interface where we can set the desired GraphQL endpoint and send queries to the server. It supports autocomplete and query validation, making it the preferred tool for many developers who work with GraphQL APIs.
Crystallize provides a single playground for all its GraphQL endpoints, which makes it very easy to switch between them and work on all APIs to cover the whole eCommerce shopping lifecycle.
Advanced GraphQL API Use-cases
“With great power comes great responsibility” - this is something we should always remember when using GraphQL APIs.
The GraphQL server's flexibility allows us to request and fetch the same data in multiple ways. Our developers are responsible for finding the optimal solution, where we ask for just what we need while trying to reduce query complexity as much as possible. Here, we’d like to cover a few different use cases, which we often consider when using the APIs but can significantly improve our website's performance.
[h3]Update Nested Item Component Data
In Crystallize, each item (product, document, or folder) uses components to store item data. The components can be as simple as a single line or numeric to more complex structural components like chunks, choices, and pieces. These components can then be placed inside the structural components, which makes it a very powerful system for content modeling.
The level of complexity increases with every level of nesting we add, and often, it is very difficult to update a single component that is nested in a few levels. To solve this problem, we use the CORE API with the `updateComponent` mutation where the `componentId` is constructed by the parent componentId-s separated by `/`.
Endpoint: api.crystallize.com/@fyour-tenant-identifier
mutation {
updateComponent(
itemId: "66b1c6fab4ddfae18c2ee0c0"
language: "en"
component: {componentId: "meta/title", singleLine: {text: "Hello World"}}
) {
... on UpdatedComponent {
updatedComponentPath
}
}
}
[h3]Searching and Filtering
Every e-shop requires good searching and filtering capabilities to help its users find what they need quickly. Here is an example of using Discover API to combine multiple filters using logical operators and even filtering on item component data.
Additionally, faceting can be set to narrow down and fine-tune the search results. Here (really), the sky's the limit, so I’d recommend reading the documentation and building your query to match your needs.
Endpoint: api.crystallize.com/your-tenant-identifier/discovery
query SearchProductsSorting {
search(
term: "chair",
options: {fuzzy: {fuzziness: DOUBLE}},
filters: {shape: {in: ["product"] } }
sorting: {variants_name:asc, price_default:desc }
)
{
hits {
... on product {
name
variants{
name
price:defaultPrice
}
}
}
}
}
[h3]Manipulating the Shopping Cart
We released the to tackle the challenge of managing the cart functionality of your e-commerce store. The Shop API handles everything, including your cart, cart-related calculations, any promotions you want to set, and much more.
Let’s look at how you can hydrate a cart with items from your Crystallize tenant and an external item. For items in Crystallize, It is as easy as providing the SKU and the quantity.
Endpoint: shop-api.crystallize.com/your-tenant-identifier/cart
mutation {
hydrate(input: {
items:[
{
sku: "robot-pink-standard",
quantity: 1
},
]
externalItems: [
{
sku:"my-shipping-cost-sku"
quantity: 1
name:"Shipping with Fedex"
variant: {
price: {
gross: 120
net:100
}
product : {
id:"my-shipping-product-id"
path:"an url"
}
}
}
]
}) {
...dataThatYouWant
}
}
Using the context field, you can further manipulate cart pricing-related information, such as the default price variant, currency, etc. The Learn section provides a comprehensive list of everything the API offers.
[h3]Working with Subscription Contracts
You also get access to our Subscription engine, which has its own API to handle creating and renewing subscriptions. It also includes the ability to track subscription usage for metric-based subscriptions.
Let’s first see how you can create a subscription contract for a customer. Please note that you need a to create a contract.
Endpoint: api.crystallize.com/your-tenant-identifier/subscriptions
mutation CREATE_SUBSCRIPTION_CONTRACT {
subscriptionContracts {
create(input: {
tenantId: "TENANT_ID"
subscriptionPlan: {
identifier: "PLAN_IDENTIFIER"
periodId: "PERIOD_ID"
}
customerIdentifier: "legolasgreenleaf@fellowship.com"
item: {
name: "ITEM_NAME"
sku: "ITEM_SKU"
}
recurring: {
price: 10.00
currency: "EUR"
}
status: {
activeUntil: "2045-12-25T00:00:00"
renewAt: "2024-10-14T00:00:00"
price: 10.00
currency: "EUR"
}
}){
id
}
}
}
Once a Subscription contract is created, this is how you would fetch the usage.
query {
subscriptionContracts {
get(id: "subscription-contract-id") {
usage(start: "2019-12-01T10:00:00Z", end: "2020-12-01T10:00:00Z") {
meteredVariableId
quantity
}
}
}
}
Based on the contract period, you would most likely have renewal events in place. The mutation just requires you to pass the contract ID:
mutation RENEW_SUBSCRIPTION_CONTRACT {
subscriptionContracts {
renew(id: "CONTRACT_ID"){
id
}
}
}
[h3]Order Management
can start by creating an order and then going further to manage it via a pipeline. The whole process requires accessing multiple APIs. The creation of an order is done via the Orders API. One thing to remember here is that the Orders API returns precisely what you provide. It will do nothing behind the scenes. The order creation mutation takes information about the cart and the customer.
Endpoint: api.crystallize.com/your-tenant-identifier/orders
mutation CREATE_ORDER {
orders {
create(
input: {
customer: {
firstName: "Legolas"
lastName: "Greenleaf"
identifier: "legolasgreenleaf@fellowship.com"
addresses: [
{
type: billing
streetNumber: "16"
street: "Greenwood"
city: "Woodland Realm"
country: "Ithilien"
postalCode: "9999"
email: "legolasgreenleaf@fellowship.com"
}
]
}
cart: {
name: "Bow of the Galadhrim"
sku: "bow-galadhrim"
imageUrl: "https://media.crystallize.com/lotr/23/1/27/6/@200/bow-galadhrim.avif"
quantity: 1
price: {
gross: 1000
net: 800
tax: { name: "Tax", percent: 25 }
currency: "EUR"
}
}
payment: {
provider: custom
custom: {
properties: { property: "payment_method", value: "Invoice" }
}
}
total: {
gross: 1000
net: 800
currency: "EUR"
tax: { name: "Tax", percent: 25 }
}
}
) {
id
}
}
}
Once an order is created, you might want to place it in a pipeline. You can configure a webhook to fire off on order creation, which can trigger the pipeline stage change mutation via the PIM API.
mutation SET_PIPELINE_STAGE {
order {
setPipelineStage(
orderId: "ORDER_ID"
pipelineId: "PIPELINE_ID"
stageId: "STAGE_ID"
) {
id
}
}
}
You can further edit or delete an order using the same API or fetch orders based on some filtering via either the PIM API or our new shiny Core API, which has more filters.
GraphQL Query Optimization
One of the things we need to do to master and become advanced GraphQL users is to know how to optimize our queries. In our experience, developers often over-fetch, asking for more information than they really need, or under-fetch, making a few smaller requests quite often in sequence.
The impact? In the first case, we ask for more data than what our app would consume, which reduces the GraphQL server's efficiency as more data has to be processed and transformed before it is sent down the wire.
In the second case, making many small requests in sequence will make your app look like it is slowly loading content.
In both cases, the performance is affected, and your end users perceived performance is downgraded.
Here are a few things to consider before you start writing your queries:
- Use query batching - Group your queries together so you don't overload the server; this way, the server can scale horizontally and handle your requests
- Query complexity analysis - Analyze your queries to ensure you are not over-fetching and that they are not too complicated. A few different tools can be used here.
- Write your queries - Don’t just copy/paste the queries you find online. Make sure you build your own to fully match your needs and requirements.
- Use pagination - GraphQL provides ways to paginate larger data sets, such as limit, cursor-based pagination, etc. Breaking up large data sets into smaller chunks helps improve performance.
- Utilizing fragments - One of the best ways to make your query more readable and optimized is to use fragments that you can share among different queries. It reduces duplication and makes your code cleaner.
- Caching - Caching is one of the most effective ways to optimize performance. You can use tools such as and to store queries and the response in an in-memory cache to speed up query performance.
Conclusion
Mastering GraphQL is not easy, but the more we think about how we use it, the better we become. Getting to know the different APIs and how to use them is the first step; using them to build your webshops is the second, and finally, being able to optimize the queries to boost the performance even further will make you a great GraphQL developer.
By the way, if you need clarification on any of your queries or mutations, we are here to help! Join our and ask away.