Getting Started with the GCA
This guide is intended for developers who want to build a GraphQL endpoint on Brightspot. For information on consuming an existing GraphQL API, see the GraphQL Explorer documentation.
The most common way to get started with GraphQL in Brightspot is with the GraphQL Content API (GCA). It automatically generates a complete GraphQL API from your content types using code as configuration for the types, fields, and functionality you want to expose. This guide will get you up and running with the GCA. This guide assumes you're familiar with the GraphQL specification. If you're new to GraphQL, you may want to check out the Learn GraphQL documentation first.
Installation
First, add the GraphQL plugin dependency to your Brightspot project. This provides the base set of APIs and tools (e.g. GraphQL Explorer) needed to work with GraphQL in Brightspot.
- Maven
- Gradle
- Gradle (Kotlin DSL)
<dependency>
<groupId>com.brightspot.graphql</groupId>
<artifactId>graphql</artifactId>
<version>1.0.5</version>
</dependency>
implementation 'com.brightspot.graphql:graphql:1.0.5'
implementation("com.brightspot.graphql:graphql:1.0.5")
Next, include the GCA, which will make the GCAEndpoint and GCASchemaSettings APIs available to your project.
- Maven
- Gradle
- Gradle (Kotlin DSL)
<dependency>
<groupId>com.brightspot.graphql</groupId>
<artifactId>gca</artifactId>
<version>1.0.5</version>
</dependency>
implementation 'com.brightspot.graphql:gca:1.0.5'
implementation("com.brightspot.graphql:gca:1.0.5")
Your first GCA endpoint
18@Recordable.DisplayName("My First GCA Endpoint")9public class MyFirstGCAEndpoint extends GCAEndpoint implements Singleton {1011@Override12public Set<String> getPaths() {13return Set.of("/my-first-gca");14}1516@Override17protected GCASchemaSettings getSchemaSettings() {18return GCASchemaSettings.newBuilder()19.build();20}21}
That's it! Upon compiling and deploying your project you will have a fully spec-compliant GraphQL API accessible at http://localhost/my-first-gca, albeit with a mostly empty schema. With just this code, you can explore the schema in the GraphQL Explorer, start furnishing API keys for access, and with the analytics plugin installed, the system will automatically collect usage statistics which are viewable in the Analytics tab of the endpoint configuration page.
Add a data model
Now let's add a content type to expose through your API. We'll create a simple Event type that represents an event with registration capabilities.
15public class Event extends Content {67@Indexed8private String name;910private String description;1112@Indexed13private Date eventDate;1415@Indexed16private String location;1718@Indexed19private Integer maxAttendees;2021@Indexed22private Integer registeredAttendees;2324@Indexed25private Double ticketPrice;2627// Getters and setters2882}83}84
Next, tell the GCA to include this type in your schema by adding it to your schema settings:
18@Recordable.DisplayName("My First GCA Endpoint")9public class MyFirstGCAEndpoint extends GCAEndpoint implements Singleton {101116@Override17protected GCASchemaSettings getSchemaSettings() {18return GCASchemaSettings.newBuilder()19.readonlyEntryClass(Event.class)20.build();21}22}
With this change, your schema now includes an Event type with all its fields, and you can query for those events with a query like:
- Query
- Response
1query EventQuery {2Query {3Records(from: {type: Event}) {4pageInfo {5offset6limit7count8}9items {10__typename11_id12... on Event {13name14description15eventDate16location17maxAttendees18registeredAttendees19
1{2"data": {3"Query": {4"Records": {5"pageInfo": {6"offset": 0,7"limit": 10,8"count": 19},10"items": [11{12"__typename": "Event",13"_id": "0000019b-a37e-ddc0-af9f-b37edded0000",14"name": "Event Name",15"description": "Event Description",16"eventDate": 1768579320000,17"location": "Event Location",18"maxAttendees": 100,19"registeredAttendees": 50,20
Enable write operations
By default, content types added with readonlyEntryClass are in fact read-only. To save, update and delete content, use mutableEntryClass instead which adds a mutation operation to the schema. You then need to specify which write actions are allowed. The example below includes them all, but you can also selectively add only those you need.
18@Recordable.DisplayName("My First GCA Endpoint")9public class MyFirstGCAEndpoint extends GCAEndpoint implements Singleton {101116@Override17protected GCASchemaSettings getSchemaSettings() {18return GCASchemaSettings.newBuilder()19.readonlyEntryClass(Event.class)20.mutableEntryClass(Event.class)21.contentActionTypesAll()22// .contentActionType(SaveActionDefinition.class)23// .contentActionType(ArchiveActionDefinition.class)24.build();25}26}
You can write GraphQL mutations to modify content like in the "save" example below:
- GraphQL Mutation
- GraphQL Variables
- GraphQL Response
1mutation EventSave($args: RecordSaveActionInput!) {2Content {3Action {4Save {5Record(args: $args) {6state {7__typename8_id9... on Event {10name11
1{2"args": {3"main": {4"EventDiff": {5"name": "New Event Name",6"description": "New Event Description",7"eventDate": 1768579320000,8"location": "New Event Location",9"maxAttendees": 100,10"registeredAttendees": 50,11"ticketPrice": 34.99,12
1{2"data": {3"Content": {4"Action": {5"Save": {6"Record": {7"state": {8"__typename": "Event",9"_id": "0000019b-a3f1-dfda-a3fb-b7fbb1140000",10"name": "New Event Name",11"description": "New Event Description",12"eventDate": 1768579320000,13"location": "New Event Location",14"maxAttendees": 100,15"registeredAttendees": 50,16"ticketPrice": 34.99,17
See the GCA Mutations documentation for more details on mutation capabilities, including other write operations, dealing with referenced and embedded records, transactions, and more.
Add a ViewModel
You've now hit a critical milestone in your GraphQL journey. It is at this point where you should seriously consider the use case you are solving for. If you refer back to the GraphQL plugin overview where it covers the various use cases for the GCA, how you configure your endpoint from here will depend heavily on those decisions. What we've done so far is great for managing ingestion and migrations where you expose all the read and write capabilities of Brightspot but would not want it to be accessible to the public. With a little more configuration, you can lock down the fields and write operations to power a frontend. However, for enterprise content delivery use cases, leveraging Brightspot's View System is a better long-term approach. You have full control over the content and shape of data available to consumers. You can also centralize business logic on the server to power multichannel delivery so that each consumer is not burdened by applying the same logic and transformations everywhere.
ViewModels allow you to add computed fields and API-specific logic without modifying your underlying data model. This is valuable because:
- Separation of concerns - Business data models stay focused on persistence while API presentation logic lives separately
- API evolution - Add new computed fields without database migrations or changing your data model
- Multiple representations - Different
ViewModelscan expose the same data model differently for different APIs - Security - Conceal sensitive data from your underlying data model
Let's create an EventViewModel that adds both presentation logic (formatting) and business logic (availability calculations):
110@ViewInterface("EventView")11public class EventViewModel extends ViewModel<Event> {1213public String getFormattedEventDate() {142324public String getFormattedPrice() {253132public Integer getAvailableSpots() {333940public Boolean getIsSoldOut() {414445public Long getDaysUntilEvent() {4653}54
Now register the ViewModel with your endpoint:
18@Recordable.DisplayName("My First GCA Endpoint")9public class MyFirstGCAEndpoint extends GCAEndpoint implements Singleton {101116@Override17protected GCASchemaSettings getSchemaSettings() {18return GCASchemaSettings.newBuilder()19.mutableEntryClass(Event.class)20.contentActionTypesAll()21.entryViewClass(EventViewModel.class)22.build();23}24}
With the ViewModel in place, you can now query the Event and fetch the computed fields.
- GraphQL Query
- GraphQL Variables
- GraphQL Response
1query GetEventView($_id: UUID) {2Get {3Record(with: {_id: $_id}) {4View(of: {type: EventView}) {5... on Record__EventView {6Response {7data {8__typename9_id10availableSpots11daysUntilEvent12formattedEventDate13formattedPrice14isSoldOut15}16}17}18}19}20}21}22
1{2"_id": "0000019b-a4b7-d394-adbf-b7bf280f0000"3}4
1{2"data": {3"Get": {4"Record": {5"View": {6"Response": {7"data": {8"__typename": "EventView",9"_id": "89962cf5c8483200821417df755f7a140000019ba4b7d394adbfb7bf280f0000",10"availableSpots": 50,11"daysUntilEvent": 6,12"formattedEventDate": "Friday, January 16, 2026 at 11:02 AM",13"formattedPrice": "$34.99",14"isSoldOut": false15}16}17}18}19}20}21}22
Notice how the ViewModel keeps your Event data model clean and raw data hidden, while the computed fields handle presentation formatting and derived business data for your API consumers. The View System provides a flexible way to transform your content models into different representations suitable for different use cases.
Add custom business logic
While GCA automatically generates standard schema types and operations to fetch them, you are beholden to those formats. In addition, there are times when you need business logic not directly tied to a specific model. You can choose to avoid the overhead that comes with writing a ViewModel or writing the query syntax required to fetch the data. You can instead include arbitrary Java methods directly in your schema.
Imagine your UI needs to display a count of upcoming events in the user's location. You can define a static method that returns the count, without creating or modifying your Event model or ViewModel.
15public final class EventQueries {67/**8* Gets the number of upcoming events in the given location.9*10* @param location The location of the event.11* @return The event count.12*/13public static long getUpcomingEventsCount(String location) {14Query<Event> query = Query.from(Event.class)15.where("eventDate >= ? && location = ?", new Date(), location);1617return query.count();18}19}20
Next, register the helper class with your endpoint configuration.
17import com.psddev.graphql.gca.settings.OperationType;89@Recordable.DisplayName("My First GCA Endpoint")10public class MyFirstGCAEndpoint extends GCAEndpoint implements Singleton {111217@Override18protected GCASchemaSettings getSchemaSettings() {19return GCASchemaSettings.newBuilder()20.mutableEntryClass(Event.class)21.contentActionTypesAll()22.entryViewClass(EventViewModel.class)23.staticMethods(OperationType.QUERY, EventQueries.class)24.build();25}26}
Now, you can invoke the method directly from GraphQL using the Execute operation like so:
- GraphQL Query
- GraphQL Variables
- GraphQL Response
1query EventQueries($location: String) {2Execute {3EventQueries {4getUpcomingEventsCount(location: $location)5}6}7}8
1{2"location": "20190"3}4
1{2"data": {3"Execute": {4"EventQueries": {5"getUpcomingEventsCount": 06}7}8}9}10
Static methods provide a flexible way to add custom business logic to your GraphQL schema with a succinct syntax that's easy for consumers to understand. See the GCA Static Methods documentation for more advanced customization options.
It is completely possible, and often preferable, to achieve the same result through the GCA's Query operation or through a custom ViewModel / method accessed from the Get operation. When to use one or the other is a matter of use case and preference. This topic is covered in more detail in later sections.
Next steps
Congratulations! You've built a complete GraphQL API with:
- Content types that map your business domain to GraphQL types
- Mutations for creating and updating content
- ViewModels that add computed fields and presentation logic
- Custom Methods that implement specialized business logic
You're ready to dive deeper on the various features and functionality that GraphQL and the GCA have to offer. Here are some recommended topics to serve as next steps.
- 🚧 GraphQL Explorer - Test queries and explore your schema
- 🚧 GCA Overview - Learn the GraphQL Content API features
- 🚧 Security - Secure your endpoints for production