--- stage: none group: unassigned info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/development/development_processes/#development-guidelines-review. title: Backend GraphQL API guide --- This document contains style and technical guidance for engineers implementing the backend of the [GitLab GraphQL API](../api/graphql/_index.md). ## Relation to REST API See the [GraphQL and REST APIs section](api_styleguide.md#graphql-and-rest-apis). ## Versioning The GraphQL API is [versionless](https://graphql.org/learn/best-practices/#versioning). ### Multi-version compatibility Though the GraphQL API is versionless, we have to be considerate about [Backwards compatibility across updates](multi_version_compatibility.md), and how it can cause incidents, like [Sidebar wasn’t loading for some users](multi_version_compatibility.md#sidebar-wasnt-loading-for-some-users). #### Mitigation To reduce the risks of an incident, on GitLab Self-Managed and GitLab Dedicated, the `@gl_introduced` directive can be used to indicate to the backend in which GitLab version the node was introduced. This way, when the query hits an older backend version, that future node is stripped out from the query. This does not mitigate the problem on GitLab.com. New GraphQL fields still need to be deployed to GitLab.com by the backend before the frontend. You can use the `@gl_introduced` directive any field, for example:
Query | Response |
```graphql fragment otherFieldsWithFuture on Namespace { webUrl otherFutureField @gl_introduced(version: "99.9.9") } query namespaceWithFutureFields { futureField @gl_introduced(version: "99.9.9") namespace(fullPath: "gitlab-org") { name futureField @gl_introduced(version: "99.9.9") ...otherFieldsWithFuture } } ``` | ```json { "data": { "futureField": null, "namespace": { "name": "Gitlab Org", "futureField": null, "webUrl": "http://gdk.test:3000/groups/gitlab-org", "otherFutureField": null } } } ``` |
Query | Response |
```graphql query fetchData { futureField @gl_introduced(version: "99.9.9") } ``` |
```json
{
"errors": [
{
"graphQLErrors": [
{
"message": "Field must have selections (query 'fetchData' returns Query but has no selections. Did you mean 'fetchData { ... }'?)",
"locations": [
{
"line": 1,
"column": 1
}
],
"path": [
"query fetchData"
],
"extensions": {
"code": "selectionMismatch",
"nodeName": "query 'fetchData'",
"typeName": "Query"
}
}
],
"clientErrors": [],
"networkError": null,
"message": "Field must have selections (query 'fetchData' returns Query but has no selections. Did you mean 'fetchData { ... }'?)",
"stack": " |
```graphql query fetchData { futureField @gl_introduced(version: "99.9.9") { id } } ``` |
```json
{
"errors": [
{
"graphQLErrors": [
{
"message": "Field must have selections (query 'fetchData' returns Query but has no selections. Did you mean 'fetchData { ... }'?)",
"locations": [
{
"line": 1,
"column": 1
}
],
"path": [
"query fetchData"
],
"extensions": {
"code": "selectionMismatch",
"nodeName": "query 'fetchData'",
"typeName": "Query"
}
}
],
"clientErrors": [],
"networkError": null,
"message": "Field must have selections (query 'fetchData' returns Query but has no selections. Did you mean 'fetchData { ... }'?)",
"stack": " |
```graphql query fetchData { project(fullPath: "gitlab-org/gitlab") { futureField @gl_introduced(version: "99.9.9") } } ``` |
```json
{
"errors": [
{
"graphQLErrors": [
{
"message": "Field must have selections (field 'project' returns Project but has no selections. Did you mean 'project { ... }'?)",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"query fetchData",
"project"
],
"extensions": {
"code": "selectionMismatch",
"nodeName": "field 'project'",
"typeName": "Project"
}
}
],
"clientErrors": [],
"networkError": null,
"message": "Field must have selections (field 'project' returns Project but has no selections. Did you mean 'project { ... }'?)",
"stack": " |