This is an open source project whose vision is to provide Salesforce CMS customers query the public CMS apis using GraphQL queries.
This project is currently integrated with
CMS delivery apis »
Table of Contents
GraphQL provides a optimized way to fetch data which involves loq latency and high throughput
Couple of motivations behind using GraphQL for CMS delivery apis are:
- Overfetching : Currently CMS Rest endpoint have a specific response schema. It ends up overfetching un-necessary metadata
- Underfetching: In some scenarios we have to fetch the id of a related entity and request another rest endpoint to get the metadata.
- Nested Queries: Ex: In order to get related contents in a channel, first we have to fetch all the channels and then make a query to fetch all the related contents. These nested queries increase the latency between the client and server
- UI components: UI components require low latency , i.e. less nested query calls. GraphQL optimizes this by creating the nested query upfront and then provide the details to the server in one go
There are other reasons because of which GraphQL has a wide adoption across the industry.
As a user I to get the channel Name, channel Type and only the managedContentId and banner image from the related content Nodes
Note this is an example of nested query. Here is how a user can achieve this
- Get the channel Info Rest API call to get the channel metadata
- Now user has to store this in an attribute
- Get the related contents User again does a rest query with the channel Id to get the related contents
Imagine if a UI component is consuming this data, couple of issues this has are
- We are overfetching channel metadata along with related content metadata
- We are performing 2 nested queries, so the client has to wait for the response from both the queries before it is able to render
- We don't have a way to define a directive like if or skip (We will discuss this with an example)
To get a local copy up and running follow these simple example steps.
- Gradle
- Intellij (Will update the command line instructions soon)
- Postman
- Create a connected app path with oauth enabled
-
Clone the repo
git clone git@github.com:salesforce/cms-graphql-apis.git
-
Open the project in Intellij
-
Go to the application.properties under src/main/resources and paste the username, password, client id and client secret (from the connected app)
-
Build and run GraphqlApiApplication
Once the project is up and running, open postman and do a post query
Postman Url: http://localhost:8081/graphql (Refer to the project snapshot image at the top)
- First let's understand what kind of objects are supported currently GraphQL query:
{
__schema {
types {
name
}
}
}
GrahQL response
{
"data": {
"__schema": {
"types": [
...
{
"name": "ManagedContentBannerImage"
},
{
"name": "ManagedContentBody"
},
{
"name": "ManagedContentExcerpt"
},
{
"name": "ManagedContentItem"
},
{
"name": "ManagedContentNodes"
},
{
"name": "ManagedContentSource"
},
{
"name": "ManagedContentTitle"
},
{
"name": "NodeTypeEnum"
},
{
"name": "Query"
},
...
]
}
}
}
- Let's say if you want to know more details of any object GraphQL query:
{
__type (name: "ManagedContentNodes"){
name
fields {
name
type {
name
kind
}
}
}
}
GraphQl response:
{
"data": {
"__type": {
"name": "ManagedContentNodes",
"fields": [
{
"name": "source",
"type": {
"name": "ManagedContentSource",
"kind": "OBJECT"
}
},
{
"name": "title",
"type": {
"name": "ManagedContentTitle",
"kind": "OBJECT"
}
},
{
"name": "bannerImage",
"type": {
"name": "ManagedContentBannerImage",
"kind": "OBJECT"
}
},
{
"name": "excerpt",
"type": {
"name": "ManagedContentExcerpt",
"kind": "OBJECT"
}
},
{
"name": "body",
"type": {
"name": "ManagedContentBody",
"kind": "OBJECT"
}
}
]
}
}
}
Once we determine the fields then couple of use cases that are currently supported (Note these are examples which might not include all the fields, consider them just an example)
GraphQL Query:
query ChannelWithContents {
channelById(channelId: "<<channelId>>"){
channelName
channelType
domainId
items (managedContentId: "<<managedContentId>>"){
managedContentId
contentNodes {
bannerImage {
mediaType
}
}
}
}
}GraphQl Response:
{
"data": {
"channelById": {
"channelName": "<<Name>>",
"channelType": "Community",
"domainId": null,
"items": [
{
"managedContentId": "<<id>>",
"contentNodes": {
"bannerImage": {
"mediaType": "Image"
}
}
},
{
"managedContentId": "<<id>>",
"contentNodes": {
"bannerImage": null
}
}
]
}
}
}
GraphQL Query:
query SearchContent {
searchContent (channelId: "0ap1R000000blKCQAY", queryTerm: "Addidas") {
contentType {
developerName
}
id
publishDate
title
urlName
}
}
GraphQL Response:
{
"data": {
"searchContent": [
{
"contentType": {
"developerName": "news"
},
"id": "20Y1R000000CaV9UAK",
"publishDate": "2020-12-28T05:57:50.000Z",
"title": "Addidas News",
"urlName": "addidas-news"
}
]
}
}
GraphQL Query:
query ChannelWithContent ($includeBanner: Boolean!){
channelById(channelId: "0ap1R000000blKCQAY"){
channelName
channelType
domainId
items{
managedContentId
contentNodes {
bannerImage @include(if: $includeBanner){
mediaType
}
}
}
}
}
Note the variable definition at the top ($includeBanner) this is an example which defines if a section is added or removed from the response
GraphQL variable payload
{
"includeBanner": false
}
GraphQL Response
{
"data": {
"channelById": {
"channelName": "Ankhi Arts",
"channelType": "Community",
"domainId": null,
"items": [
{
"managedContentId": "20Y1R000000CaV9UAK",
"contentNodes": {}
},
{
"managedContentId": "20Y1R000000001EUAQ",
"contentNodes": {}
}
]
}
}
}
See the open issues for a list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request