Content Delivery API

The Content Delivery API gives clients access to their Brightspot ViewModel, and is the best way to use Brightspot as a headless CMS to power your custom frontend.

This API is available for all Sites hosted by your Brightspot application. Each Site has a unique domain or sub-domain through which you can access the API Endpoint. For each Site, the API lives at the path /delivery/graphql. So, if you have a site hosted at https://example.com, and you want to access the API, you need to send requests to the URL https://example.com/delivery/graphql. The API will only return content that is accessible to that Site as determined by the Site and Content specific permissions in the CMS.

The operations you can perform are all strictly typed, and there is just one query operation for each type. The total number of types represented in the API is roughly equal to the total number of concrete ViewModel classes plus the total number of non-concrete ViewModel classes that are annotated with @ViewInterface. There are certain conditions that must be met for a ViewModel class to be represented and therefore the actual number can be less. Here is a templated snippet of a generated schema, where _ViewModelType_ represents a single ViewModel class in the system.

schema {
  query: Query
}

type Query {
  PageEntryView(id: ID, path: String): com_psddev_cms_view_PageEntryView
  _ViewModelType_(id: ID, path: String): _ViewModelType_
  # repeat for each content type
}

union PageEntryView = _ViewModelType_

# One for each View type
type _ViewModelType_ {
  _modelId: ID
  _modelType: ID
  # ViewModel specific fields here
}

PageEntryView is a special union type that represents any type that has a path associated with it. This is useful when you are requesting a path and you don’t know what type of content lives at that path.

The query operation, named after the ViewModel type it returns, accepts either an ID or a path as input. The ID or path in this case belongs to the backing data model. All ViewModels are backed by some Model. The subset of those that are represented in the query API are backed by a Data Model, which is a Recordable object that’s stored in the database and has an ID (and optionally a path or “permalink”) associated with it. That is to say, the ViewModel itself does not have its own ID, only a class identifier.

Consider the following example:

Backend Code

public class Article extends Content {
    @Indexed
    public String title;
}

public class Foo { }

@ViewInterface
public class ArticleViewModel extends ViewModel<Article> implements PageEntryView {

    public String getHeadline() {
        return model.title + "!!!";
    }

    public FooViewModel getFoo() {
        return this.createView(FooViewModel.class, new Foo());
    }
}

@ViewInterface
public class FooViewModel extends ViewModel<Foo> {

    public String getBar() {
        return "Baz";
    }
}

Generated Schema

type Query {
  PageEntryView(id: ID, path: String): PageEntryView
  ArticleViewModel(id: ID, path: String): ArticleViewModel
}

union com_psddev_cms_view_PageEntryView = ArticleViewModel

type ArticleViewModel {
  _modelId: ID
  _modelType: ID
  headline: String
  foo: FooViewModel
}

type FooViewModel {
  bar: String
}

Example Query

query SimpleViewModelExample {
  PageEntryView(path: "/article-example") {
    __type
    ... on ArticleViewModel {
      _modelId
      _modelType
      headline
      foo {
        bar
      }
    }
  }
}

Sample Response

{
  "data": {
    "PageEntryView": {
      "__typename": "ArticleViewModel",
      "_modelId": "99157c6c-224f-42e9-b5c9-3ad8b40e16cc",
      "_modelType": "0000015d-56b9-d2db-a5dd-f6fd6ecb0003",
      "headline": "Article Example!!!",
      "foo": {
        "__typename": "FooViewModel",
        "bar": "Baz"
      }
    }
  }
}

In the above example, a query API was not generated for FooViewModel because the backing Model class Foo is not Recordable. There were also no _model* fields generated in the FooViewModel schema type for the same reason.