- Introduction
- Understanding a Decorator
- Graphweaver Decorators
- The Entity Decorator
- The Field Decorator
- The RelationshipField Decorator
- The Hook Decorator
- The MediaField Decorator
Introduction
When working with Graphweaver, you'll often come across scenarios where you need to add additional functionality or behaviour to your entities.
This is where decorators come into play, allowing you to easily extend and customise your code.
Understanding a Decorator
Before we dive into usage, let's take a moment to understand what decorators are. In Graphweaver, decorators are functions that modify the behaviour of a class or its members, such as properties or methods.
They are prefixed with an @
symbol and applied using the decorator syntax.
Graphweaver Decorators
The Entity
Decorator
This decorator is used to link the entity to the GraphQL API. There are two options when using the decorator. The first is the name of the entity as it will appear in the GraphQL API. The second option allows you to configure the entity.
Let’s look at how we use it:
@Entity('Task', {
provider: new MikroBackendProvider(OrmTag, myConnection),
})
export class Tag extends GraphQLEntity<OrmTag> {
public dataEntity!: OrmTag;
@Field(() => ID)
id!: string;
@Field(() => String)
name!: string;
@RelationshipField<Task>(() => [Task], { relatedField: 'tags' })
tasks!: Task[];
}
The entity decorator takes the following options:
description
- A description of the entity and is exposed to the GraphQL API.plural
- By default the entity with be pluralised if possible. Such asusers
but alsofish
will get pluralised tomultipleFish
. You can override this default using this value.provider
- This is the data provider that is called to resolve the data for this entity.apiOptions
- Options that change how the entity appears in the API.apiOptions.clientGeneratedPrimaryKeys
- Defaultfalse
: Set this option totrue
if you need Graphweaver to require clients to provide the primary key value when creating this entity. By default we expect the underlying datasource to generate the primary key, for example a database identity or serial column. This option is useful when you are using client generated uuids or similar for your IDs.apiOptions.excludeFromBuiltInOperations
- Defaultfalse
: If set totrue
this entity will not be given the default list, find one, create, update, and delete operations in the GraphQL schema. Setting this totrue
also enablesapiOptions.excludeFromFiltering
.apiOptions.excludeFromBuiltInWriteOperations
- Defaultfalse
: If set totrue
this entity will not be given the default create, update, and delete operations in the GraphQL schema.apiOptions.excludeFromFiltering
- Defaultfalse
: If set totrue
, Graphweaver will not provide filtering arguments for the entity in the default operations.apiOptions.excludeFromTracing
- Defaultfalse
: If set totrue
, there will be no recorded telemetry for this entity when queried from the Admin UI. This is used internally to prevent us entering a telemetry loop for the telemetry entities themselves, but can also be useful if you want to exclude your own entities from telemetry recording. Note that in order to achieve this from your own front ends, you need to set theX-GRAPHWEAVER-SUPPRESS-TRACING: true
header on your requests, which is what this option instructs the admin UI to do for you.apiOptions.namespaceForFederation
- Defaultfalse
: When set totrue
, if federation is enabled, this entity will have the federation subgraph name joined into it in the GraphQL schema so that it will not clash with other entities of the same name from other subgraphs that do not have the same data set that this entity does. This is used internally forMedia
entities, for example, so that if multiple Graphweaver subgraphs are composed in a federation context the data they have for their own Media uploads will not be expected by the router to be an identical set.apiOptions.resolvableViaFederation
- Defaulttrue
: In Federation v2, an entity can specify that it is not resolvable in this subgraph to the federation router. This is used to link to other entities that are not in your subgraph, but for which you have the ID value. If you set this tofalse
, we will emit@key(fields: "your primary key field", resolvable: false)
in the_service { sdl }
query result for the federation router. You should do this for the providerless reference entities that are there for linking via federation that you don't actually have the data for in this Graphweaver instance.apiOptions.excludeFromFederation
- Defaultfalse
: If set totrue
, this entity will not be returned at all in calls to_service { sdl }
for the federation router. In most cases it'd be better to use@inaccessible
, but in some cases you truly do want a private entity that is usable in your Graphweaver instance but is not part of the schema we tell the federation router about.adminUIOptions
- Options that change how the entity appears in the Admin UI.adminUIOptions.defaultFilter?: Filter<G>;
- Specifies the default filter used in the Admin UI when viewing the list view. Users can still override this filter, but this is the default. This has no impact on the API, purely how entities are displayed in the Admin UI.adminUIOptions.exportPageSize?: number;
- Specifies how many entities should be requested per page during a CSV export. If not set, the default is 200.adminUIOptions.hideInSideBar?: boolean;
- If true, the entity will not show up in the side bar (on the left when you log in). If a property on another entity references this entity, it will still show up in the field of that entity using its display property.adminUIOptions.hideInFilterBar?: boolean;
- If true, properties that reference this entity from other entities will not be able to be filtered in the list view for those entities.adminUIOptions.summaryField?: Extract<keyof G, string>;
- Specifies what field on this entity should be used to summarise it in the Admin UI when referenced from other entities, e.g. if a Task in a to-do list application has an 'assigned to' field that references User, if the summary field on User is set to "name", then users in the task list in the admin area will be shown by their name. If no summary field is set for an entity, we default to showing the 'id' field.adminUIOptions.readonly?: boolean;
- If true, the entity will not be editable in the Admin UI. This is useful for entities that are managed by some other system or we don't want a user to update from the adminUI.
The Field
Decorator
This decorator is used to define a field on an entity. There are two options that can be passed to a field, the first is a function that returns the entity type. The second is a options object that allows you to configure the field. Here is an example:
@ObjectType('Tag')
export class Tag extends GraphQLEntity<OrmTag> {
public dataEntity!: OrmTag;
@Field(() => ID)
id!: string;
@Field(() => String, { readonly: true, defaultValue: 'test' })
name!: string;
}
In the above the Tag
entity has a name
field that is read only and a default value of “test”.
The field decorator takes the following options:
description?: string;
- A description of the field and is exposed to the GraphQL API.deprecationReason?: string;
- A description of why the field is deprecated and this description is exposed to the GraphQL API.defaultValue?: any;
- Use this to set the value for this field if one is not provided by the user.nullable?: boolean;
- Can this field be null.excludeFromFilterType?: boolean;
- This will exclude the filter from the API filter argument.readonly?: boolean;
- This marks the field as read only in the API.adminUIOptions.hideInTable?: boolean;
- This will hide the field in the table inside the admin UI.adminUIOptions.hideInFilterBar?: boolean;
- This will remove the field from the filter bar on the admin UI.adminUIOptions.readonly?: boolean;
- This marks the field as read only only in the admin UI.adminUIOptions.summaryField?: boolean;
- This marks the field as the display field within the admin UI.apiOptions.excludeFromBuiltInWriteOperations?: boolean;
- This marks the field as read only in the API.
The RelationshipField
Decorator
This decorator is used to link one entity to another. There are two options linking to a collection of entities or linking to one entity. Let’s look at each one in-turn starting with linking to a collection.
To link to a collection you would use the relatedField
property. Here is an example:
@ObjectType('Tag')
export class Tag extends GraphQLEntity<OrmTag> {
public dataEntity!: OrmTag;
@Field(() => ID)
id!: string;
@Field(() => String)
name!: string;
@RelationshipField<Task>(() => [Task], { relatedField: 'tags' })
tasks!: Task[];
}
In the above the Tag
entity has many Task
s and the related field on the Task
entity is tags
.
Now let’s look at linking to a single entity:
@ApplyAccessControlList(acl)
@ObjectType('Task')
export class Task extends GraphQLEntity<OrmTask> {
public dataEntity!: OrmTask;
@Field(() => ID)
id!: string;
@Field(() => String)
description!: string;
@RelationshipField<Task>(() => User, { id: 'userId' })
user!: User;
}
In the above example the Task
entity has one User
entity attached and here we need to link the id of the user entity. The userId
here is the column name on the data entity that holds the user ID field.
The Hook
Decorator
The Hook
decorator is used to “hook” into lifecycle events for an entity such as beforeRead or afterUpdate.
See the hook section for more information.
The MediaField
Decorator
The @MediaField
decorator is used to describe a field that represents media.
Graphweaver supports uploading media to a storage provider, and rendering images in the admin ui. To do this, Graphweaver supplies a storage provider class and a MediaField decorator to connect and describe the media being uploaded. An example of using AWS S3 to handle images can be seen in the examples folder.
MediaField accepts:
storageProvider
- A provider that supplies access to the underlying storage.
An example of a storage provider and usage of the @MediaField
decorator can be seen below:
const s3 = new S3StorageProvider({
bucketName: process.env.AWS_S3_BUCKET,
region: process.env.AWS_REGION,
type: StorageType.S3,
expiresIn: 3600,
endpoint: process.env.AWS_S3_ENDPOINT,
});
@Entity('Submission', {
provider: new MikroBackendProvider(OrmSubmission, pgConnection),
})
export class Submission extends GraphQLEntity<OrmSubmission> {
public dataEntity!: OrmSubmission;
@Field(() => ID)
id!: string;
@MediaField({ storageProvider: s3 })
image?: Media;
}