summaryrefslogtreecommitdiff
path: root/doc/development/api_graphql_styleguide.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/api_graphql_styleguide.md')
-rw-r--r--doc/development/api_graphql_styleguide.md95
1 files changed, 89 insertions, 6 deletions
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 18fc0fb7d33..3d4c033e676 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -49,6 +49,20 @@ See also:
- [Exposing Global IDs](#exposing-global-ids).
- [Mutation arguments](#object-identifier-arguments).
+We have a custom scalar type (`Types::GlobalIDType`) which should be used as the
+type of input and output arguments when the value is a `GlobalID`. The benefits
+of using this type instead of `ID` are:
+
+- it validates that the value is a `GlobalID`
+- it parses it into a `GlobalID` before passing it to user code
+- it can be parameterized on the type of the object (e.g.
+ `GlobalIDType[Project]`) which offers even better validation and security.
+
+Consider using this type for all new arguments and result types. Remember that
+it is perfectly possible to parameterize this type with a concern or a
+supertype, if you want to accept a wider range of objects (e.g.
+`GlobalIDType[Issuable]` vs `GlobalIDType[Issue]`).
+
## Types
We use a code-first schema, and we declare what type everything is in Ruby.
@@ -371,8 +385,8 @@ end
GitLab's GraphQL API is versionless, which means we maintain backwards
compatibility with older versions of the API with every change. Rather
than removing a field or [enum value](#enums), we need to _deprecate_ it instead.
-In future, GitLab
-[may remove deprecated parts of the schema](https://gitlab.com/gitlab-org/gitlab/-/issues/32292).
+The deprecated parts of the schema can then be removed in a future release
+in accordance with [GitLab's deprecation process](../api/graphql/index.md#deprecation-process).
Fields and enum values are deprecated using the `deprecated` property.
The value of the property is a `Hash` of:
@@ -472,6 +486,28 @@ end
Enum values can be deprecated using the
[`deprecated` keyword](#deprecating-fields-and-enum-values).
+### Defining GraphQL enums dynamically from Rails enums
+
+If your GraphQL enum is backed by a [Rails enum](creating_enums.md), then consider
+using the Rails enum to dynamically define the GraphQL enum values. Doing so
+binds the GraphQL enum values to the Rails enum definition, so if values are
+ever added to the Rails enum then the GraphQL enum automatically reflects the change.
+
+Example:
+
+```ruby
+module Types
+ class IssuableSeverityEnum < BaseEnum
+ graphql_name 'IssuableSeverity'
+ description 'Incident severity'
+
+ ::IssuableSeverity.severities.keys.each do |severity|
+ value severity.upcase, value: severity, description: "#{severity.titleize} severity"
+ end
+ end
+end
+```
+
## JSON
When data to be returned by GraphQL is stored as
@@ -780,6 +816,25 @@ to advertise the need for lookahead:
For an example of real world use, please
see [`ResolvesMergeRequests`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/graphql/resolvers/concerns/resolves_merge_requests.rb).
+### Negated arguments
+
+Negated filters can filter some resources (for example, find all issues that
+have the `bug` label, but don't have the `bug2` label assigned). The `not`
+argument is the preferred syntax to pass negated arguments:
+
+```graphql
+issues(labelName: "bug", not: {labelName: "bug2"}) {
+ nodes {
+ id
+ title
+ }
+}
+```
+
+To avoid duplicated argument definitions, you can place these arguments in a reusable module (or
+class, if the arguments are nested). Alternatively, you can consider to add a
+[helper resolver method](https://gitlab.com/gitlab-org/gitlab/-/issues/258969).
+
## Pass a parent object into a child Presenter
Sometimes you need to access the resolved query parent in a child context to compute fields. Usually the parent is only
@@ -827,10 +882,39 @@ mutation.
### Building Mutations
-Mutations live in `app/graphql/mutations` ideally grouped per
+Mutations are stored in `app/graphql/mutations`, ideally grouped per
resources they are mutating, similar to our services. They should
inherit `Mutations::BaseMutation`. The fields defined on the mutation
-will be returned as the result of the mutation.
+are returned as the result of the mutation.
+
+#### Update mutation granularity
+
+GitLab's service-oriented architecture means that most mutations call a Create, Delete, or Update
+service, for example `UpdateMergeRequestService`.
+For Update mutations, a you might want to only update one aspect of an object, and thus only need a
+_fine-grained_ mutation, for example `MergeRequest::SetWip`.
+
+It's acceptable to have both fine-grained mutations and coarse-grained mutations, but be aware
+that too many fine-grained mutations can lead to organizational challenges in maintainability, code
+comprehensibility, and testing.
+Each mutation requires a new class, which can lead to technical debt.
+It also means the schema becomes very big, and we want users to easily navigate our schema.
+As each new mutation also needs tests (including slower request integration tests), adding mutations
+slows down the test suite.
+
+To minimize changes:
+
+- Use existing mutations, such as `MergeRequest::Update`, when available.
+- Expose existing services as a coarse-grained mutation.
+
+When a fine-grained mutation might be more appropriate:
+
+- Modifying a property that requires specific permissions or other specialized logic.
+- Exposing a state-machine-like transition (locking issues, merging MRs, closing epics, etc).
+- Accepting nested properties (where we accept properties for a child object).
+- The semantics of the mutation can be expressed clearly and concisely.
+
+See [issue #233063](https://gitlab.com/gitlab-org/gitlab/-/issues/233063) for further context.
### Naming conventions
@@ -1361,5 +1445,4 @@ For information on generating GraphQL documentation and schema files, see
[updating the schema documentation](rake_tasks.md#update-graphql-documentation-and-schema-definitions).
To help our readers, you should also add a new page to our [GraphQL API](../api/graphql/index.md) documentation.
-For guidance, see the [GraphQL API](documentation/styleguide.md#graphql-api) section
-of our documentation style guide.
+For guidance, see the [GraphQL API](documentation/graphql_styleguide.md) page.