Understanding Content Type Schema
This reference explains how your Java content type classes map to GraphQL schema types in the GCA. Understanding this mapping is essential for crafting effective queries and mutations: it covers the transformation from Java field definitions to GraphQL field types, how relationships and embedded objects are represented, what metadata fields the GCA includes automatically, and the schema directives that carry Brightspot-specific metadata.
Which types end up in the schema
The schema starts from your configured entry types (readonlyEntryClass / mutableEntryClass) and crawls outward: every record-typed field's possible types are visited until the full reachable graph is covered. A configured supertype automatically pulls in its concrete subtypes—configuring CreativeWork exposes Article, Gallery, and Video if they extend it.
Three settings shape the crawl:
referenceTypeFilter(...)— controls which types may be resolved through reference fields. By default, only entry types are resolvable as references; everything else returns a restricted reference (see Restricted references). Embedded types are always accessible through their parent.fieldFilter(...)— include/exclude individual fields from the schema.richTextElementTypeFilter(...)— controls which rich-text element types are included.
Type naming
A type's GraphQL name is its Java simple class name (Article). On conflict, the package name is used to disambiguate (Article_bar), with entry types getting naming priority. You can take full control with objectTypeTypeNameFunction(...). Every generated type carries a @bsp_type(internalName: ...) directive linking it back to the Dari ObjectType, so consumers never need to guess.
Anatomy of a generated type
A content type's fields come in three groups—globals, declared fields, and modifications:
1type Article implements Record {2# Globals — present on every record3_id: ID4_label: String5_globals: Globals!6_urls: URLs!78# Declared fields — from the Java class (and superclasses)9headline: String10body: String11tags: [String]12author: Author13leadImage: StorageItem1415# Modifications — one field per modified type16TaggableMod: TaggableMod17}
Global fields
| Field | Description |
|---|---|
_id | The record's UUID. |
_label | The record's display label, as shown in the CMS. |
_globals | Container for global modifications—data attached to all records (e.g. SEO fields). Each global modification appears as a field on the Globals type. |
_urls | The record's URL data—permalink and paths—when URL support applies. |
_preview | A StorageItem for the record's preview image. Only on types whose Java class declares @PreviewField, expressed via the RecordPreview interface. |
_raw | The raw state as JSON. Only present with allowRawStateAccess()—bypasses all field filters, so enable with care. |
Every record type implements the Record interface, which declares the global fields—handy for writing fragments that work across all types.
Declared fields
Java fields map to GraphQL fields using the Java field name. Getter methods registered as ObjectMethods (e.g. annotated @Ignored(false)) also become fields, named by stripping the get/is/has prefix and lower-casing (getFoo() → foo).
When a field and its getter would produce the same name, the GCA merges them into one field. At runtime the merged field returns the getter's value by default—after all, the getter may apply business logic. To read the raw stored value instead, use the @bsp_raw query directive:
1{2Query {3Records(from: {type: Article}) {4items @bsp_raw(if: true) { # raw values for all fields below...5... on Article {6headline7body @bsp_raw(if: false) # ...except this one8}9}10}11}12}
Field-level documentation flows through automatically: Javadoc comments on your Java fields become GraphQL descriptions, visible in the Explorer and SDL.
Modifications
Dari Modifications appear as one field per modified type, named <ModifiedType>Mod (e.g. TaggableMod: TaggableMod). If exactly one modification targets the type, its fields are presented on that type; with multiple modifications, the Mod type contains one field per modification class.
If you prefer modification fields without the extra nesting level when there's only one modification, enable inlineModificationFieldsWhenOnlyOneTypeExists()—but note that adding a second modification later will then change the schema shape and break existing queries.
Relationships and interfaces
References
A reference field's GraphQL type depends on what can legally appear there:
- A single possible concrete type → the type itself (
author: Author). - Multiple possible types → an interface named for the context:
<Type>Entry(entry-field results),<Type>Ref(reference fields), or<Type>Embed(embedded fields). Query across them with shared interface fields, or use... onfragments for type-specific fields. - Disparate types declared via
@Types→ a field-level interface named<Type>_<field>Ref/<Type>_<field>Embed, which only carries the global fields.
Restricted references
When a reference points to a type your endpoint configuration does not allow resolving (per referenceTypeFilter), the GCA returns a RecordRef object instead of the record: _id and _type are populated, everything else is null. The caller learns that a reference exists—and to what—without gaining access to its data.
Embedded types
Types annotated @Embedded (like our Address example) are stored inside their parent. They are always traversable when the parent is accessible, and in query-complexity calculations embedded traversal is free, whereas reference traversal costs—references require database fetches.
Field type mapping
| Java type | GraphQL type | Notes |
|---|---|---|
String | String | Text fields with @Values constraints become enums. |
boolean / Boolean | Boolean! / Boolean | Primitives are non-null in output, never required in input. |
int / Integer | Int! / Int | Same primitive/boxed rule applies to all numbers. |
long / Long | Long! / Long | Custom scalar—GraphQL's Int is 32-bit. |
double / Double | Float! / Float | GraphQL Float is double-precision. |
byte, short, float | Byte, Short, Float | Custom scalars for the rest of the numeric tower. |
java.util.Date | Date | Serialized as epoch milliseconds. |
java.time.* | Instant, LocalDate, Duration, ... | ISO-8601 strings. See Built-in Types. |
UUID | UUID | String form. |
URI / URL / Locale | Uri / Url / Locale | Validating string scalars. |
List<T> / Set<T> | [T] | Sets serialize as lists. disallowNullCollectionItems() makes items non-null. |
Map<String, T> | MapOf<T> | Object with entries, get(key), and json fields. |
StorageItem | StorageItem | Files/images: publicUrl, contentType, path, metadata(key), HTTP headers. Binary data is not inlined—download via the URL. |
Location | GeoPoint | {latitude, longitude}. |
Region | GeoArea | Polygons/circles, GeoJSON, and WKT representations. |
Rich text (@ToolUi.RichText) | Marked text | Plain text plus a list of marks (annotations) with positions—see Built-in Types. |
Recordable subtypes | object type / interface | See Relationships. |
Metric | — | Not supported. |
Mutation input types follow the same mapping with input-flavored counterparts (GeoPointInput, StorageItemInput, <Type>DiffRefInput for references, <Type>Diff for embedded diffs), described in Content mutations.
Schema directives
The GCA annotates the schema with directives that make it self-describing. They're visible in the SDL and queryable through standard introspection:
| Directive | Where | Purpose |
|---|---|---|
@bsp_type(id, internalName, embedded, abstract) | types & interfaces | Links the GraphQL type to its backing ObjectType. |
@bsp_field(internalName, indexed, unique, embedded) | fields | Links a field to its ObjectField. indexed: true marks the fields usable in query predicates and sorts. |
@bsp_method(internalName, indexed, unique, embedded) | fields | Same, for fields backed by an ObjectMethod. |
@bsp_getter | fields | Marks a merged field whose value comes from a getter; @bsp_raw applies to these. |
@bsp_raw(if: Boolean!) | queries | Fetch raw state instead of getter values. Cascades to child fields until overridden. |
These directives are what make tooling like query builders possible without hardcoding type knowledge—see Type Introspection for the higher-level API.
Next steps
- Built-in Types — full scalar and type reference
- Get Single Content Items and Query Content Collections — the operations that return these types
- Type Introspection — discover types and fields programmatically