--- 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: Build and deploy real-time view components --- GitLab provides an interactive user experience through individual view components that accept user input and reflect state changes back to the user. For example, on the Merge Request page, users can approve, leave comments, interact with the CI/CD pipeline, and more. However, GitLab often does not reflect state updates in a timely manner. This means parts of the page display stale data that only update after users reload the page. To address this, GitLab has introduced technology and programming APIs that allow view components to receive state updates in real-time over a WebSocket. The following documentation tells you how to build and deploy view components that receive updates in real-time from the GitLab Ruby on Rails server. {{< alert type="note" >}} Action Cable and GraphQL subscriptions are a work-in-progress and under active development. Developers must evaluate their use case to check if these are the right tools to use. If you are not sure, ask for help in the [`#f_real-time` internal Slack channel](https://gitlab.slack.com/archives/CUX9Z2N66). {{< /alert >}} ## Working Safely with WebSockets WebSockets are a relatively new technology at GitLab and you should code defensively when using a WebSocket connection. ### Backwards Compatibility Treat the connection as ephemeral and ensure the feature you're building is backwards compatible. Ensure critical functionality degrades gracefully when a WebSocket connection isn't available. You can work on the frontend and backend at the same time because updates over WebSockets are difficult to simulate without the necessary backend code in place. However, always deploy backend changes first. It is strongly advised to package the backend and frontend changes in separate releases or to manage rollout with a Feature Flag, especially where a new connection is introduced. This ensures that when the frontend starts subscribing to events, the backend is already prepared to service them. ### New Connections at Scale Introducing a new WebSocket connection is particularly risky at scale. If you need to establish a connection on a new area of the site, perform the steps detailed in the [Introduce a new WebSocket Connection](#introduce-a-new-websocket-connection) section before going further. ## Build real-time view components Prerequisites: Read the: - [GraphQL development guide](fe_guide/graphql.md). - [Vue development guide](fe_guide/vue.md). To build a real-time view component on GitLab, you must: - Integrate a Vue component with Apollo subscriptions in the GitLab frontend. - Add and trigger GraphQL subscriptions from the GitLab Ruby on Rails backend. ### Integrate a Vue component with Apollo subscriptions {{< alert type="note" >}} Our current real-time stack assumes that client code is built using Vue as the rendering layer and Apollo as the state and networking layer. If you are working with a part of the GitLab frontend that has not been migrated to Vue + Apollo yet, complete that task first. {{< /alert >}} Consider a hypothetical `IssueView` Vue component that observes and renders GitLab `Issue` data. For simplicity, we assume here that all it does is render an issue's title and description: ```javascript import issueQuery from '~/issues/queries/issue_view.query.graqhql'; export default { props: { issueId: { type: Number, required: false, default: null, }, }, apollo: { // Name of the Apollo query object. Must match the field name bound by `data`. issue: { // Query used for the initial fetch. query: issueQuery, // Bind arguments used for the initial fetch query. variables() { return { iid: this.issueId, }; }, // Map response data to view properties. update(data) { return data.project?.issue || {}; }, }, }, // Reactive Vue component data. Apollo updates these when queries return or subscriptions fire. data() { return { issue: {}, // It is good practice to return initial state here while the view is loading. }; }, }; // The