> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/collaboration/realtime-collaboration-server.md).

# Setting up your server for real-time collaboration

Learn how to build a server for realtime collaboration using Firebase. Follow step-by-step instructions to create, authenticate, and manage data with Firebase's Database Rules. Optimize your server fo

In realtime collaboration, a server will merely act as an online database that triggers events upon data creation/modification/deletion. As long as the above requirement is met, your server can be built in any language and stack of your choice. For the simplicity of this guide, we will be using [Firebase](https://firebase.google.com/).

1. Go to the [Firebase Console](https://console.firebase.google.com/), login and create a project.
2. Click "Add Firebase to your Web App" and copy the whole code for "Initializing Firebase". If `storageBucket` is empty, close the popup and try again (that's a known bug from Firebase).
3. Create a JavaScript file and name it `server.js`.
4. Paste the code that you have copied from Firebase. (Note that you should remove the script tags)
5. Store the [firebase.database.References](https://firebase.google.com/docs/reference/js/v8/firebase.database.Reference) for annotations and users. We will use these to create/update/delete data, and listen to data change events as well.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
window.Server = function() {
  const config = {
    apiKey: "YOUR_API_KEY",
    authDomain: "PROJECT_ID.firebaseapp.com",
    databaseURL: "https://PROJECT_ID.firebaseio.com",
    storageBucket: "PROJECT_ID.appspot.com",
    messagingSenderId: "YOUR_SENDER_ID"
  };
  firebase.initializeApp(config);

  this.annotationsRef = firebase.database().ref().child('annotations');
  this.authorsRef = firebase.database().ref().child('authors');
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Create a custom bind function for authorization and data using [firebase.auth.Auth#onAuthStateChanged](https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#onAuthStateChanged) and [firebase.database.Reference#on](https://firebase.google.com/docs/reference/js/v8/firebase.database.Reference#on).

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Server.prototype.bind = function(action, callbackFunction) {
  switch(action) {
    case 'onAuthStateChanged':
      firebase.auth().onAuthStateChanged(callbackFunction);
      break;
    case 'onAnnotationCreated':
      this.annotationsRef.on('child_added', callbackFunction);
      break;
    case 'onAnnotationUpdated':
      this.annotationsRef.on('child_changed', callbackFunction);
      break;
    case 'onAnnotationDeleted':
      this.annotationsRef.on('child_removed', callbackFunction);
      break;
    default:
      console.error('The action is not defined.');
      break;
  }
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Define a method to check if author exists in the database. We will use [firebase.database.Reference#once](https://firebase.google.com/docs/reference/js/v8/firebase.database.Reference#once) and [firebase.database.DataSnapshot#hasChild](https://firebase.google.com/docs/reference/js/v8/firebase.database.DataSnapshot#hasChild) to do so.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Server.prototype.checkAuthor = function(authorId, openReturningAuthorPopup, openNewAuthorPopup) {
  this.authorsRef.once('value', authors => {
    if (authors.hasChild(authorId)) {
      this.authorsRef.child(authorId).once('value', author => {
        openReturningAuthorPopup(author.val().authorName);
      });
    } else {
      openNewAuthorPopup();
    }
  });
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. Define a sign-in method. In this guide, we will use [firebase.auth.Auth#signInAnonymously](https://firebase.google.com/docs/reference/js/v8/firebase.auth.Auth#signInAnonymously).

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Server.prototype.signInAnonymously = function() {
  firebase.auth().signInAnonymously().catch(error => {
    if (error.code === 'auth/operation-not-allowed') {
      alert('You must enable Anonymous auth in the Firebase Console.');
    } else {
      console.error(error);
    }
  });
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

1. From the Firebase console click the "Authentication" button on the left panel and then click the "Sign-in Method" tab, just to the right of "Users". From this page click the "Anonymous" button and choose to enable Anonymous login.
2. Define data-write methods using [firebase.database.Reference#set](https://firebase.google.com/docs/reference/js/v8/firebase.database.Reference#set) and [firebase.database.Reference#remove](https://firebase.google.com/docs/reference/js/v8/firebase.database.Reference#remove).

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Server.prototype.createAnnotation = function(annotationId, annotationData) {
  this.annotationsRef.child(annotationId).set(annotationData);
};

Server.prototype.updateAnnotation = function(annotationId, annotationData) {
  this.annotationsRef.child(annotationId).set(annotationData);
};

Server.prototype.deleteAnnotation = function(annotationId) {
  this.annotationsRef.child(annotationId).remove();
};

Server.prototype.updateAuthor = function(authorId, authorData) {
  this.authorsRef.child(authorId).set(authorData);
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

Last but not least, you should add server-side permission rules for writing data. Although client-side permission checking is supported in WebViewer, every user does have access to each annotation's information (including authorId and authorName). Thus, data-write permission should be regulated in the server as well. In this guide, we have used Firebase's [Database Rules](https://firebase.google.com/docs/database/security/).

1. Copy the JSON below and paste it in your Firebase Console's Database Rules. From the console click the "Database" button on the left panel and then click the "Rules" tab, just to the right of "Data". This will make sure that trying to modify someone else's annotation isn't allowed.

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
{
  "rules": {
    ".read": "auth != null",

    "annotations": {
      "$annotationId": {
        ".write": "auth.uid === newData.child('authorId').val() || auth.uid === data.child('authorId').val() || auth.uid === newData.child('parentAuthorId').val() || auth.uid === data.child('parentAuthorId').val()"
      }
    },

    "authors": {
      "$authorId": {
        ".write": "auth.uid === $authorId"
      }
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/collaboration/realtime-collaboration-server.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
