Schemas
Learn how to write high-quality GraphQL schemas for your models.
Overview
ComposeDB models are written in GraphQL using GraphQL Schema Definition Language (SDL). Your schema defines a collection of object types and the relationships between them. Those types will have scalars (values), shapes (key-value mappings), and lists to describe the structure and validation rules for the model, and use directives for other metadata information.
We currently support a subset of SDL’s scalars and directives, but are continually adding more, see the API reference for a complete list.
Concepts
Learn about key concepts for the GraphQL Schema Definition Language.
On this page, we provide basic info for you to begin writing GraphQL data models. For more complete information on the GraphQL Schema Definition Language, visit the GraphQL website.
Shapes, Fields, Scalars
The most basic component in a GraphQL schema is an object type, sometimes called a shape. It simply represents the shape of the data you want to query and its properties, consisting of fields (keys) and scalars (values).
type EducationModule {
module_name: String!
module_domain: String
number_of_topics_covered: Int!
learners_enrolled: [Learner!]!
}
Where:
type
defines a new objectEducationModule
the name given to the object; queryablemodule_name
,module_domain
,number_of_topics_covered
andlearners_enrolled
are fields in theEducationModule
type; all fields are queryableString!
andInt!
define the data type of the value. By adding!
to the end of the type declaration, we are telling GraphQL to always return a value when we query this field, which also means that when writing data through a mutation a value is required.[Learner!]!
defines the data type of the value, but in this case the data type is an array of another type,Learner
, which is not depicted above. It is required since it includes the!
.
Enums
Enums represent the type of a single string value in the schema from a set of accepted values, for example:
enum NoteStatus {
DEFAULT
IMPORTANT
ARCHIVE
}
Special Types
GraphQL reserves the use of two special type names, query
and mutation
.
Do not name any of your own custom types, which are the majority of the types you will work with, the same as these two special types.
query
type is used as the entry point when retrieving data using GraphQLmutation
type is used as the entry point when writing or changing data using GraphQL
Embedded Shapes
Our first shape, EducationModule
, makes use of an embedded shape called Learner
:
type EducationModule {
module_name: String!
module_domain: String
number_of_topics_covered: Int!
learners_enrolled: [Learner!]!
}
type Learner {
first_name: String!
last_name: String!
username: String!
}
Learner
is not anything different from EducationModule
in terms of how it is defined. It does contain different fields, but it is just a GraphQL shape that can be used like any other shape.
💡 For this to work, you will want to define both shapes inside the same GraphQL file when writing ComposeDB schemas.
Interfaces
Interfaces are abstract models defining common fields for other models. Objects can implement these interfaces to ensure they match their constraints and provide additional relations options, for example:
interface TextContent @createModel(description: "Required text content interface") {
text: String! @string(maxLength: 10000)
}
type Page implements TextContent @createModel(description: "Page model") {
title: String @string(maxLength: 100)
text: String! @string(maxLength: 10000)
}
type Post implements TextContent @createModel(description: "Post model") {
title: String! @string(maxLength: 100)
text: String! @string(maxLength: 10000)
createdAt: DateTime!
}
Directives
ComposeDB comes with a list of different directives that can be used to create or load data models, define type validation rules, and create indices for specific fields which enables them to be used for document filtering and sorting.
Type validation directives
Directives are keywords that add validation rules to a scalar. Not all scalars need to have directives, though Strings are required to have a maxLength. Let’s add directives to the two shapes used in this guide:
type EducationModule {
module_name: String! @string(maxLength: 50)
module_domain: String @string(minLength: 5, maxLength: 50)
number_of_topics_covered: Int! @int(min: 1, max: 100)
learners_enrolled: [Learner!]! @list(maxLength: 30)
}
type Learner {
first_name: String! @string(minLength: 10, maxLength: 30)
last_name: String! @string(maxLength: 30)
username: String! @string(maxLength: 32) @immutable
}
Where:
- Each directive is declared using the
@
symbol @string
adds validation rules to values that are strings, e.g. minimum and maximum length@int
adds validation rules to values that are integers, e.g. minimum and maximum values@list
adds validation rules to an array, e.g. maximum length@immutable
ensures that after a field value is set it won't be updated
Directives for creating indices
To be able to filter the query results by a specific field and sort them in a specific order,
you are required to create indices for corresponding fields. In ComposeDB indices work the
same way as in traditional databases - they speed up the querying processes. You can create indices
for specific fields using @createIndex
directive as follows:
type Posts
@createModel(accountRelation: LIST, description: "A simple Post")
@createIndex(fields: [{ path: "title" }])
@createIndex(fields: [{ path: "tag" }])
@createIndex(fields: [{ path: "created_at" }]) {
title: String! @string(minLength: 1, maxLength: 100)
body: String! @string(minLength: 1, maxLength: 100)
tag: String! @string(minLength: 1, maxLength: 100)
ranking: Int!
created_at: DateTime!
}
The example above will create indices for the fields title
, tag
and created_at
, and will enable you to filter the documents based on the values in these fields as well as sort the results in a specified order.
You can create indices for individual or multiple fields in your data models.