Advanced GraphQL API Usage in Crystallize
GraphQL has been known as a query language that allows you to fetch the data you need, thus reducing the payload size and improving performance. Today, we will cover a few advanced GraphQL queries that will help you enhance your eCommerce functionality.
Before we start - all the endpoints can be found in the GraphQL playground mentioned below.
Since Crystallize began, GraphQL 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. The Crystallize GraphQL application programming interfaces (API) efficiently manage core shopping functionalities like product catalogs, cart management, checkout processes, and orders.
The best place to test a query or check a schema is the GraphQL playground. 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.
We will use the Crystallize GraphQL playground to demo all advanced GraphQL 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.
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
}
}
}
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
}
}
}
}
}
We released the Shop API 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.
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 Subscription plan 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
}
}
}
Order management in Crystallize 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.
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 Apollo and TanStack to store queries and the response in an in-memory cache to speed up query performance.
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 Slack community and ask away.
😎Dig in GraphQL More
Benefits of Using GraphQL for Your eCommerce Business
To begin, it's essential to make sure we understand what GraphQL is. It's a technology developed by Facebook in 2012 to address their issues with mobile platforms. One of the technologies they were trying to use, REST, was not performing well in their particular setup, so they decided to replace it with a homegrown solution. GraphQL was the result. A few years later, the GraphQL specification was made open-source so that anyone could potentially benefit from it and contribute to it.
GraphQL is a query language for APIs (application programming interfaces) and a server for executing those queries. APIs are the means by which computer programs communicate with each other to exchange and manipulate data.
REST is the most-used API architecture by far (as Postman's recent API report shows | graph is below); however, GraphQL adoption is on the rise, and many already are using it in place of REST to facilitate communication with all the different APIs that an application may have to talk to.
How to use GraphQL API with generated TypeScript Types?
Using GraphQL types in a frontend application avoids unexpected bugs and errors. There are code generators to automatically generate GraphQL types from a given API.
GraphQL Typescript Generators
GraphQL is a query language that optimizes performance by returning only the requested data, while TypeScript is a statically-typed language that helps catch errors during development. Together they can create robust and efficient web applications.