Understand GraphQL Schema Requirements

The Salesforce Connect adapter for GraphQL requires that the GraphQL schema adhere to a specific format.

Required Editions

Available in: both Salesforce Classic and Lightning Experience (not for high-data-volume external objects)

Available in: Developer Edition

Available for an extra cost in: Enterprise, Performance, and Unlimited Editions

This is a representative schema to understand how to construct a schema that works with Salesforce Connect. Notations to understand the representative schema:

  • ObjName: Denotes the name of a GraphQL object.
  • Bold text: Represents the pattern to follow for each object.
  • Non-bold text: Represents common code, not specific to the objects.
type Query {
    objName(
        limit: Int,
        offset: Int,
        orderBy: [ObjName_OrderByInput],
        where: ObjName_FilterInput,
        first: Int,
        after: String
    ): ObjName_Connection
        <additional object queries...>
        node(id: ID!): Node
}

type Mutation {
    create_ObjName(input: ObjName_CreateInput!): ObjName
    <additional create mutations>
    ...
    delete_ObjName(id: ID!): ObjName
    <additional delete mutations>
    ...
    update_ObjName(input: ObjName_UpdateInput!): ObjName
    <additional update mutations>
    ...
}

type ObjName implements Node {
    id: ID!
    <fieldName: fieldType>
    ...
}

type ObjName_Connection {
    edges: [ObjName_Edge]
    pageInfo: PageInfo!
}

input ObjName_CreateInput {
        <fieldName: fieldType>
        ...
}

input ObjName_UpdateInput {
    id: ID!
    <fieldName: fieldType>
    ...
}

type ObjName_Edge {
    cursor: String!
    node: ObjName
}

input ObjName_FilterInput {
    and: [ObjName_FilterInput]
    not: ObjName_FilterInput
    or: [ObjName_FilterInput]
    id: IDOperator
    <fieldName: fieldTypeOperator>
    ...
}

input ObjName_OrderByInput {
    id: OrderByClause
    <fieldName >: OrderByClause
    ...
}

interface Node {
    id: ID!
}

enum NullsOrder {
    NULLS_FIRST
    NULLS_LAST
}

input OrderByClause {
    direction: Direction
    nulls: NullsOrder
}

type PageInfo {
    endCursor: String
    hasNextPage: Boolean!
    hasPreviousPage: Boolean!
    startCursor: String
}

input StringOperator {
    eq: String
    gt: String
    ge: String
    in: [String]
    like: String
    lt: String
    le: String
    ne: String
    nin: [String]
}

input AWSDateOperator {
    eq: AWSDate
    gt: AWSDate
    ge: AWSDate
    in: [AWSDate]
    like: AWSDate
    lt: AWSDate
    le: AWSDate
    ne: AWSDate
    nin: [AWSDate]
}

input AWSDateTimeOperator {
    eq: AWSDateTime
    gt: AWSDateTime
    ge: AWSDateTime
    in: [AWSDateTime]
    like: AWSDateTime
    lt: AWSDateTime
    le: AWSDateTime
    ne: AWSDateTime
    nin: [AWSDateTime]
}

input AWSJSONOperator {
    eq: AWSJSON
    gt: AWSJSON
    ge: AWSJSON
    in: [AWSJSON]
    like: AWSJSON
    lt: AWSJSON
    le: AWSJSON
    ne: AWSJSON
    nin: [AWSJSON]
}

input AWSTimeOperator {
    eq: AWSTime
    gt: AWSTime
    ge: AWSTime
    in: [AWSTime]
    like: AWSTime
    lt: AWSTime
    le: AWSTime
    ne: AWSTime
    nin: [AWSTime]
}

input BooleanOperator {
    eq: Boolean
    gt: Boolean
    gebolded: Boolean
    in: [Boolean]
    like: Boolean
    lt: Boolean
    le: Boolean
    ne: Boolean
    nin: [Boolean]
}

enum Direction {
    ASC
    DESC
}

input FloatOperator {
    eq: Float
    gt: Float
    ge: Float
    in: [Float]
    like: Float
    lt: Float
    le: Float
    ne: Float
    nin: [Float]
}

input IDOperator {
    eq: ID
    gt: ID
    ge: ID
    in: [ID]
    like: ID
    lt: ID
    le: ID
    ne: ID
    nin: [ID]
}

input IntOperator {
    eq: Int
    gt: Int
    ge: Int
    in: [Int]
    like: Int
    lt: Int
    le: Int
    ne: Int
    nin: [Int]
}

Each object must have an ID! field that’s globally unique and this global ID must be same across all the objects. If you have multiple fields of type ID! and there isn’t field with id as the field name, use keyColumns qualifier to specify the global ID. See Qualifier Options.

When an external lookup relationship represents the same lookup relationship that exists between GraphQL objects, keep in mind these considerations. The external ID field on the parent external object must match the format of the external lookup field of the child external object. In case they don’t match, use qualifiers to manage one of the following:

  • Manipulate the parent external object’s external ID to match the child external lookup.
  • Create a virtual column in the child external object to match the parent external ID.

It’s important that the GraphQL global ID and Salesforce external ID can be computed from one another. A possible way to structure a global ID is to use a prefix to namespace the ID and keep it unique. If the primary key of the object is composed of multiple key fields, use a delimiter to separate the prefix and the ID. If any of the key fields’ value contains the delimiter, make sure to escape the delimiter.

In this example, ObjName| is the prefix and - is the delimiter. Using | after the ObjName helps to parse the prefix from the key composition.

id: ObjName|KEY1\-123-KEY2\-456
key1: KEY1-123
key2: KEY2-456