Build an End-To-End Encrypted Chat App in Nuxt.js: Messages and Encryption
Tags: typescript, javascript, appwrite, nuxt
Published: 2022-10-24 18:13:04

Encryption involves converting information into secret codes to protect the information’s precise details from being readable to unauthorized people, including cyber-criminals.

This article discusses building an end-to-end encrypted chat application in Nuxt.js, using [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) to provide the database service and [Cryptr](https://www.npmjs.com/package/cryptr) for encrypting the exchanged data.

## GitHub

Check out the complete source code [here](https://github.com/MoeRayo/end-to-end-chat-encryption).

## Prerequisites

Understanding this article requires the following:

- Installation of Node.js
- Basic knowledge of TypeScript
- Docker [installation](https://www.docker.com/get-started/)
- An Appwrite instance; check out this [article](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) on how to set up an instance

Moreover, this article is the second part of a two-part series on building an end-to-end encrypted chat app in Nuxt.js. The first part of the article that discusses **Setup** and **Authentication** can be found [here](https://dev.to/hackmamba/build-an-end-to-end-encrypted-chat-app-in-nuxtjs-setup-and-authentication-2o14).

## Setting up Appwrite’s database

[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=hackmamba-blog) is an end-to-end backend server that provides services that help abstract away some of the complexity of performing backend tasks. One of the services offered by Appwrite is the **Database Service**, which allows us to store users’ data and fetch them as needed. 

### Creating a Database
To create a database in Appwrite, click on the **Database** option in the menu tab, where we can add a database to the system.

![Creating a Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u7xcfe0kbe80q4td6o8j.png)

### Creating a collection
Appwrite’s database services also provide collection services within its database. These collections act as containers for documents. To create a collection, select the desired database from which we can add a new collection.

![Creating a collection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xd9nuhlnso0i47ty4s5.png)

After creating a new collection, the system redirects us to the **Settings** page, from which we can set the permissions needed to use the database service — an empty permission field will prevent the client from accessing the document. In our case, we will add the role access of “any” to allow any role access to the collection. As seen in the image below:

![collection settings ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f9uqgegrw0p110te2lbu.png)

### Creating attributes
Our document will have some peculiar attributes. To create them, we will navigate to the **Attributes** section. In our case, we will choose the **New String Attribute**.


![collection settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4b1p9820jidapi5kiiq.png)

We can then create an **Attribute ID** with the name “message” with a size of 256 bits.


![attributes setting](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ull0p5uxv7h73e21o5u.png)

## Connecting the chat app with Appwrite’s database

We will start by importing Appwrite’s SDK initialized in the `init.js` and Appwrite’s `database` method. This is done as follows:

```javascript
import {client} from '~/init'
import { Databases, ID } from 'appwrite';
```

We will also update the `data` property in the `script` tag with `message`, `databaseMessages`, and `password` to hold the user’s and database’s messages, like so:

```javascript
data: () => ({
    message: '',
    databaseMessages: ['']
  }),
```

Then, we will create an instance of `databases` using the imported `databases` method from Appwrite, which takes the client as parameters. 

```javascript
const databases = new Databases(client);
```
**Listing messages in the database**
We can then create a `listMessages` function in the `methods` property like so:
```javascript
listMessages: async function (){
      let promise = await databases.listDocuments('database ID', 'collection ID');
      this.databaseMessages = promise.documents.map(document =>  document.message)
    }, 
```
The `listMessages` function in the code block above does the following:

- `listDocuments` method accesses the `databases` method from Appwrite to list all documents in the database.
- The `listDocuments` method takes the `database ID` and `collection ID` as parameters. These IDs can be found on the Appwrite dashboard.
- Returned messages in the Appwrite database are stored in `this.databaseMessages` .

### Sending messages to the database
After the above, we can send messages to the Appwrite database by creating a function called `sendMessage` in the methods:

```javascript
sendMessage: async function(){
  try {
    await databases.createDocument('632754bf9b28a3d032e7', '632754dccfc24bf0505e', ID.unique(), {'message': this.message})
    alert('message sent')
    this.message = '';
    this.listMessages()
  } catch(e){
    console.log(e)
  }
}
```
The `sendMessage` function in the code block above does the following:

- the `createDocument` method accesses the `databases` method from Appwrite to create a new document in the database. This method takes the `database ID`, `collection ID`, Appwrite’s `ID.unique()` method, and the attribute value(s) as parameters. In this case, our attribute value is `message` , which we created on the Appwrite admin dashboard.
- An alert that confirms successful message sending.
- Clears out the message input field, and we call the `listMessages()` function, which lists all the messages that were sent to the Appwrite’s database

Next, we’ll update our chat application interface to render all the messages sent to the database as follows:

```javascript
<section v-for="databaseMessage in databaseMessages" :key="JSON.stringify(databaseMessage)">
  <p :class="databaseMessage ? ['bg-lightest-blue', 'pa2', 'mv2', 'br3', 'w-60', 'f7'] : ''">
    {{databaseMessage}}
  </p>
</section>
```

The code block above loops through the messages returned from the database using Vue’s `v-for` directive. Individual messages are then rendered with `{{databaseMessage}}`.

Putting it all together, our `Pages/Chat.vue` file at this stage will look like this: **[Follow this GitHub Gist](https://gist.github.com/MoeRayo/27157fca9d8a05f856d10092f0283630)**.

Our application interface will also look like the below after sending our first message:


![first message interface](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6w40ty19yhzqo7vw6fmu.png)

## Encryption

We will use [Cryptr](https://www.npmjs.com/package/cryptr), which is an encryption and decryption aes-256-gcm module for Node.js to encrypt the messages we will be sending on our chat app.

Start using it by installing it in the project like so:

```javascript
npm install cryptr
```

We can then import the Cryptr library and create a new instance of Cryptr in our `Pages/Chat.vue`, with the new Cryptr instance containing our Cryptr’s secret key. 

```javascript
const Cryptr = require('cryptr');
const cryptr = new Cryptr('*****');
```
We want to encrypt the messages we are sending and get back the decrypted version, as the encrypted messages do not have much meaning to the reader. To achieve this, we will modify the `sendMessage` function in the `Pages/Chat.vue` with the following:

```javascript
sendMessage: async function(){
  const encryptedMessage = cryptr.encrypt(this.message)
  try {
    await databases.createDocument('632754bf9b28a3d032e7', '632754dccfc24bf0505e', ID.unique(), {'message': encryptedMessage})
    
  } catch(e){
    console.log(e)
  }
}
```
The code block above does the following:

- We access the `encrypt` method on the Cryptr library and pass the message being sent as a parameter. This `encrypt` method encrypts the message and stores it in the `encryptedMessage` variable.
- The data in the `encryptedMessage` is then passed to Appwrite to be saved in the database.

If we check our Appwrite database, we will see the “How are you?” message we sent encoded with a bunch of numbers and alphabets. We will also see the first message (Hello) — in a human-readable format.

![human readable format message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sy58jzw61bp7h22eeiih.png)

Since humans can’t make sense of the encrypted message, we will need to decrypt the message before it is rendered back in the chat interface. To do this, we will edit the `listMessages` function in the `Pages/Chat.vue`, like so:

```javascript
listMessages: async function (){
  let promise = await databases.listDocuments('632754bf9b28a3d032e7', '632754dccfc24bf0505e');
  this.databaseMessages = promise.documents.map(document =>  cryptr.decrypt(document.message))
}, 
```
We fetched the messages from Appwrite’s database in the above code using the `databases.listDocuments` method. We then used Cryptr’s `decrypt` method to transform the encrypted message we fetched from the database.

At this point, we have successfully created a chat application with end-to-end encryption to keep our conversations secure.
Our application will look like the below:


![final application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hfkbr1ojyyp7igbq4o4.gif)


## Resources
- [Create a local Appwrite instance in 3 steps](https://dev.to/hackmamba/create-a-local-appwrite-instance-in-3-steps-19n9?utm_source=hackmamba&utm_medium=hackmamba) 
- [Appwrite’s database services](https://appwrite.io/docs/databases?utm_source=hackmamba&utm_medium=hackmamba-blog)