GraphQL Genie works off regular GraphQL type definitions with some additional features
GraphQL Genie supports interfaces and unions! You may want to look into using the @storeName
custom directive and see special concerns in altering your schema when using them.
- @unique
- The @unique directive marks a scalar field as unique. All id fields will be considered marked @unique
- This is used for various update operations that need to find a unique field, errors will be thrown if a duplicate value is attempted to be added on a unique field
- Currently GraphQL Genie does not automatically add indexes to unique fields in your database and it is recommended that you do this manually to improve performance.
- @relation(name: String)
- The directive @relation(name: String) can be attached to a relation field
- Fields with relations will have referential integrity and inverse updates
- Genie will compute relations automatically (like between User and Address below) but if the relation is ambiguous the @relation directive should be used
- If a related object is deleted, it's related nodes will be set to null
- @default(value: String!)
- The directive @default(value: String!) sets a default value for a scalar field.
- Note that the value argument is of type String for all scalar fields
- @connection
- The directive @connection can be put on a list field to turn it into a type following the Relay Cursor Connections Specification rather than just returning a normal list.
- @model
- By default any object type will be part of the CRUD model, using the @model directive will limit to just types that use this directive
- @createdTimestamp (allowManual: Boolean = false)
- The field will be automatically set when the record is created
- Must be of type DateTime
- If you want to also be able to allow the field to be set manually in mutations set allowManual to true
- @updatedTimestamp (allowManual: Boolean = false)
- The field will automatically be updated when the record is updated
- Must be of type DateTime
- If you want to also be able to allow the field to be set manually in mutations set allowManual to true
- @storeName(value: String!)
- If you want the actual name of the type in your backend store to be something other than based off the type name. Using this you can effectively rename your type without actually migrating any data
- Interfaces and Unions
- With Genie when using Interfaces and Unions only one actual backend store type will be created for an Interface/Union. For example if you had the interface
Animal
and two typesDog
andCat
that implement it will create the actual store typeAnimal_Cat_Dog
. The backend type is a alphabatized union separated by _. - If in the future you add a
Monkey
genie will then useAnimal_Cat_Dog_Monkey
as the store name, which would make you unable to access data created previously. In this case you want to use the @storeName directive and set it toAnimal_Cat_Dog
. Or if you predicted this possibility you could set the store name on the interface toAnimal
and never worry about adding new types that implement that interface.interface Animal @storeName(value:"Animal") {...
- With Genie when using Interfaces and Unions only one actual backend store type will be created for an Interface/Union. For example if you had the interface
- You can see your current store types by checking the
recordTypes
property from the store in your data resolver.genie.getDataResolver().getStore().recordTypes
In addition to the default scalar types (Int, Float, String, Boolean, ID) GraphQL Genie comes built in with scalar fields Date, Time, DateTime (learn more) and JSON (learn more).
interface Submission {
id: ID! @unique
text: String!
author: User @relation(name: "SubmissionsByUser")
}
type Story implements Submission @model {
id: ID! @unique
title: String!
text: String!
author: User @relation(name: "SubmissionsByUser")
likedBy: [User!] @connection @relation(name: "LikedSubmissions")
createdAt: DateTime @createdTimestamp
lastUpdatedAt: DateTime @updatedTimestamp
}
type Comment implements Submission @model {
id: ID! @unique
text: String!
author: User @relation(name: "SubmissionsByUser")
approved: Boolean @default(value: "true")
}
type User @model {
id: ID! @unique
email: String @unique
submissions: [Submission!] @relation(name: "SubmissionsByUser")
address: Address
liked: [Submission!] @connection @relation(name: "LikedSubmissions")
}
type Address @model {
id: ID! @unique
city: String!
user: User
}
Adding types and fields to types or interfaces for example don't require any additional work, genie will take care of creating the necessary tables and fields.
Note that if you add a non-null field you may get errors querying that field on data that existed before that field existed (returning null on non null type) unless you update every record of that data to add a value first.
If you are adding a new type that implements an interface or is part of a union you may need to use the @storeName
custom directive in order to make sure your past data and new data are in the same place of your store.
If you are adding a single existing type to a new union or interface you will similarly have to use the @storeName
custom directive.
You will need to script a migration of data in changes to field names/types or that result in data that is currently stored in multiple places needing to be stored under a single type. This is the case if you are:
- Changing a field name
- Changing a field type
- Adding an existing type to an existing interface or union
- Adding multiple existing types to a new or existing union or interface
One way to migrate the data
- Backup all your data in some way (you can use the data export function or query), just in case something goes wrong
- Or do this in staging
- Create two genie objects, one with the old schema the new schema and no data
- Export data from the old schema genie by calling
getRawData
or using theexportData
mutation. Pass in the types that have been added to an interface or union. - Make any alterations to the data that are necessary such as
- Changing a field name or value
- Adding values to new non-null fields
- If the new schema is pointing to the same database, delete old type data (from types you exported) (you can use the deleteMany mutation)
- Import data into the new schema genie by calling
importRawData
or using theimportData
mutation and pass in the result of the export.