Types
The Quickstart presented a first example of a Warpgrapher configuration, shown again here.
static CONFIG: &str = "
version: 1
model:
- name: User
props:
- name: email
type: String
required: false
";
Type Configuration
Recall that the version
value is used to indicate the configuration file format version to be used. Right now, the only valid value is 1. The next element in the configuration a a data model. The model
object is a list of types. The example shown in the Quickstart uses many defaults for simplicity. The definition below shows the full range of options for property definitions. Don't worry about relationships between types for the moment. Those are covered in the next section.
model:
- name: String
props:
- name: String
uses:
create: Boolean
query: Boolean
update: Boolean
output: Boolean
type: String # Boolean | Float | ID | Int | String
required: Boolean
list: Boolean
resolver: String
validator: String
endpoints:
read: Boolean
create: Boolean
update: Boolean
delete: Boolean
Right under the model object is a list of types. The first attribute describing a type is a name. In the example from the Quickstart, the name of the type is User
.
The second attribute describing a type is props
. The props
attribute is a list of properties that are stored on nodes of that type. Each property is described the several configuration attributes, as follows.
The name
attribute is a string that identifies the property. It must be unique within the scope of the type. In the Quickstart example, the sole property on the User type is named email.
The uses
attribute is an object that contains four fields within it, create
, query
, update
, and output
, each a boolean value. The fields within the uses
attribute control whether the property is present in various parts of the GraphQL schema. If the create
attribute is true, then the property will be included in the GraphQL input for creation operations. If false, the property will be omitted from creation operations. If the query
attribute is true, the property will be included in the GraphQL schema for search query input. If false, the property will be omitted from search query operations. If the update
attribute is true, the property will be included in the GraphQL schema input for updating existing nodes. If false, the property will be omitted from the update schema. Lastly, if the output
attribute is true, the property will be included in the GraphQL schema for nodes returned to the client. If false, the property will be omitted from the output.
By default, all uses
boolean attributes are true, meaning that the property is included in all relevant areas of the GraphQL schema. Selectively setting some of the uses
attributes handles uses cases where a property should not be available for some operations. For example, one might set the create
attribute to false if a property is a calculated value that should never be set directly. One might set update
to false to make an attribute immutable -- for example, the email
property of the User
type might have update
set to false if GraphQL clients should not be able to tamper with the identities of users. One might set output
to false for properties that should never be read through the GraphQL interface, such as for keeping people from reading out a password property.
The type
attribute of the property definition is a String value that must take on a value of Boolean
, Float
, ID
, Int
, or String
, defining type of the property.
If the required
attribute of the property definition is false, the property is not required (it is optional). By default this attribute is true, which means it must be provided when nodes of this type are created (unless hidden from the create
use) and it must be present (non-null) when retrieving the node from Warpgrapher (again, unless hidden from the output
use).
If the list
attribute of the property definition is true, the property is a list of scalar values of type
. If list
is false, the property is only a single value of that scalar type.
The resolver
attribute is a text key that is used to identify a custom-written resolver function. Warpgrapher allows applications to define custom resolvers that do more or different things than the default CRUD operations automatically provided by Warpgrapher itself. For example, a custom resolver might dynamically calculate a value, such as a total or average, rather than just returning a value from the database. Custom resolvers for dynamic properties are covered in greater detail later in the book.
The validator
attribute is a text key that is used to identify a fuction that validates an input. For example, a validation function might check an email against and email validation regex. Validation functions are covered in greater detail later in the book.
Note that the endpoints
attribute is on the type
definition, not the property
definition, as indicated by the indentation in the YAML example above. The endpoints
attribute is somewhat similar to the uses
boolean, but at the level of the whole type rather than a single property. If the read
attribute is true, Warpgrapher will generate a query in the GraphQL schema so that node of this type can be retrieved. If false, no query will be generated. If the create
attribute is true, Warpgrapher will generate a node creation mutation in the GraphQL schema. If false, no creation mutation will be generated. If the update
attribute is true, Warpgrapher will generate a node update mutation in the GraphQL schema. If false, no update mutation will be generated. Lastly, if the delete
attribute is true, Warpgrapher will generate a node deletion mutation in the GraphQL schema. If false, no delete mutation will be generated.
Generated Schema
Warpgrapher uses the configuration described above to automatically generate a GraphQL schema and default resolver to create, read, update, and delete nodes of the types defined in the configuration's model section. The remainder of this section walks through the contents of the schema in detail.
The top level GraphQL Query has two queries within it, as shown below. The _version
query returns a scalar String
with the version of the GraphQL service. The value returned is set with the with_version method on the EngineBuilder
.
type Query {
User(input: UserQueryInput, options: UserOptions): [User!]
_version: String
}
The User
query, above, is generated by Warpgrapher for the retrieval of User nodes. The query takes two parameters, an input
parameter that provides any search parameters that narrow down the set of Users to be retrieved, and an options
object. The query returns a User
type.
The UserQueryInput
, defined in the schema snippet below, is use to provide search parameters to identify the User
nodes to return to the client. The User
node configuration had only one property, email
. Warpgrapher automatically adds an id
property that contains a unique identifier for nodes. In the GraphQL schema, the id is always represented as a string. However, in some Gremlin back-ends, the id may be required to be an integer, in which case the id field in the GraphQL schema will be a String that can be successfully parsed into an integer value.
input UserQueryInput {
email: StringQueryInput
id: StringQueryInput
}
Note that the types of both email
and id
are StringQueryInput
, not a simple String
scalar. This is because the query input allows for more than just an exact match.
input StringQueryInput {
CONTAINS: String
EQ: String
GT: String
GTE: String
IN: [String!]
LT: String
LTE: String
NOTCONTAINS: String
NOTEQ: String
NOTIN: [String!]
}
The StringQueryInput
has various options for matching a String more flexibly than an exact match. The CONTAINS
operator looks for the associated String value anywhere in the target property (e.g. the email
or id
properties of a User
node). EQ
looks for an exact match. GT
and GTE
are greater-than and great-than-or-equals, which are useful for searching for ranges based on alphabetization, as do LT
and LTE
. The IN
operators allows for searching for any string that is within a given set of Strings. NOTCONTAINS
is the opposite of CONTAINS
, looking for property values that do not contain the provided String. NOTEQ
looks for non-matching Strings. And finally, NOTIN
matches property values that do not appear in the provided set of Strings.
The options
argument, described back above as an argument for the User
query as a whole, is of type UserOptions
. The UserOptions
type has a single property, called sort
, which is a list of zero or more UserSort
objects. Each UserSort
object has two enumeration properties, direction
and orderBy
.
type UserOptions {
sort: [UserSort!]
}
type UserSort {
direction: DirectionEnum
orderBy: UserOrderByEnum!
}
enum DirectionEnum {
ascending
descending
}
enum UserOrderByEnum {
id
email
}
The UserOrderByEnum
has variant values for each of the properties (but not relationships) on a User. By including one or more values in the sort
array provided to UserOptions
, it is possible to sort results coming back from Warpgrapher. The direction
property determines whether the results are returned in ascending or descending sort order. The orderBy
field determines on which property the results are sorted. If the sort
array contains more than one value, then resorts groups of results with the same first sort key are further sorted by the second key, and so on. For example, a sort
array might have entries for joinDate
and then name
to sort first by the date someone joined, and alphabetically for all people who joined on the same date.
The results of the query are returned in a User
type, shown below.
type User { email: String id: ID! }
The `User` type is the definition of the output type for the `User` GraphQL query. The names are the same, but these are two distinct things in the GraphQL schema -- the `User` query returns an array of zero or more `User` types. The `User` type is two fields, and `id` and an `email`. The id is a unique identifier for that node, which may be an integer or a UUID, depending on the graph database used. The `email` string is the single property that was defined on the example schema.
type Mutation { UserCreate(input: UserCreateMutationInput!, options: UserOptions): User UserDelete(input: UserDeleteInput!, options: UserOptions): Int UserUpdate(input: UserUpdateInput!, options: UserOptions): [User!] }
In addition to providing queries to retrieve existing nodes, Warpgrapher also automatically generates GraphQL schema elements and resolvers for create, update, and delete operations. The schema snippet above shows the mutations that are generated for the `User` node in the example configuration. All three of the mutations take an `options` argument, which was described in the section on queries, above. Additionally, all three mutations take an `input` value, that provides the information necessary to complete the create, update, or delete operation, respectively. Creation operations return the created node. Update operations return all the nodes that were matched and updated. Lastly, the delete operation returns the number of nodes that were deleted. The input arguments are detailed below.
input UserCreateMutationInput { email: String id: ID }
The `UserCreateMutationInput` mutation input includes the email property defined in the example configuration. It also includes an `id` property. Note that the `id` property is optional. If not provided by the client, it will be set to a unique identifier by the Warpgrapher server. The reason that clients are permitted to set the `id` when creating nodes is to allow for offline mode support, which may require the creation of identifiers within local caches that should remain the same after synchronization with the server.
input UserDeleteInput { DELETE: UserDeleteMutationInput MATCH: UserQueryInput }
input UserDeleteMutationInput
The `UserDeleteInput` input is used to identify which nodes to delete. Note that the `MATCH` part of the argument is the very same `UserQueryInput` type used in the `User` query schema element above. So searching for which nodes to delete is the same input format used to search for nodes to return in a read query. The `UserDeleteMutationInput` is empty right now, and may be omitted. It will become relevant later, in the discussion on relationships between nodes.
input UserUpdateInput { MATCH: UserQueryInput SET: UserUpdateMutationInput }
input UserUpdateMutationInput { email: String }
Lastly, the `UserUpdateInput` input is provided to the udpate mutation in order to select the nodes that need to be updated and describe the update to be applied. The `MATCH` attribute is used to identify what nodes require the update. Note that the type of the `MATCH` attribute is `UserQueryInput`, which is the same type used for searching for nodes in the GraphQL query above. The `SET` attribute is used to provide the new values to which the matching nodes should be set. In this example, it is a single String value for the `email` of the `User`. Note that `id`s are set only at creation. They cannot be updated later.
## Full Schema Listing
The full schema, described in pieces above, is included below:
input UserDeleteInput { DELETE: UserDeleteMutationInput MATCH: UserQueryInput }
input UserQueryInput { email: StringQueryInput id: StringQueryInput }
type Mutation { UserCreate(input: UserCreateMutationInput!, options: UserOptions): User UserDelete(input: UserDeleteInput!, options: UserOptions): Int UserUpdate(input: UserUpdateInput!, options: UserOptions): [User!] }
input UserUpdateMutationInput { email: String }
type Subscription
input UserUpdateInput { MATCH: UserQueryInput SET: UserUpdateMutationInput }
type Query { User(input: UserQueryInput, options: UserOptions): [User!] _version: String }
input UserDeleteMutationInput
type User { email: String id: ID! }
input UserCreateMutationInput { email: String id: ID }
input StringQueryInput { CONTAINS: String EQ: String GT: String GTE: String IN: [String!] LT: String LTE: String NOTCONTAINS: String NOTEQ: String NOTIN: [String!] }