Appwrite with Angular SSR
Tags: opensource, tutorial, webdev, angular
Published: 2021-07-20 10:05:42

> Thanks to `@Caryntjen` from Discord for bringing this problem up

## Table Of Contents
  * [Introduction](#introduction)
  * [Setup Angular project with Appwrite](#setup-angular-project-with-appwrite)
  * [Add Angular Universal to our project](#add-angular-universal-to-our-project)
  * [Connect Appwrite to Angular Universal](#connect-appwrite-to-angular-universal)

---

## Introduction <a name="introduction"></a>

Server-side rendering can help your website speed up the initial load and let bots access your dynamic data to improve SEO. This article will show you how to quickly solve a problem with Appwrite data not being load before rendering a page server-side.

To solve our problem, we will use the library `angular-zen`. This library will create a zone.js task under the hood, and that helps Angular Universal understand your async code. To learn more about this, you can visit their docs: [Angular zen docs](https://bs-angular-zen.web.app/docs/zen/injectables/RouteAware.html#resolveInMacroTask)

> In SSR, the server doesn't wait for the async code to complete. The result is scrapers and search engines receiving a page without resolved data, which is bad in case you need them to read some resolved metadata tags for example. Use resolveInMacroTask() to have your server block and wait for resolves before rendering.

## Setup Angular project with Appwrite <a name="setup-angular-project-with-appwrite"></a>

Before solving the problem, let's see the problem! We start by creating an empty angular project:
```bash
ng new appwrite-ssr
cd appwrite-ssr
```
```
? Do you want to enforce stricter type checking and stricter bundle budgets in the workspace?
  This setting helps improve maintainability and catch bugs ahead of time.
  For more information, see https://angular.io/strict Yes
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS
```
> Make sure to pick the same options to have the same results

Now, let's write some Appwrite code.  To use Appwrite in our frontend project, we need to install its client-side SDK:
```bash
npm i appwrite
```

This is a simple javascript/typescript library with no connection to Angular, so we don't need to worry about importing modules or injecting services. For simplicity, we will do everything in our `app.component`. Still, it is strongly recommended to put all Appwrite logic into a separate `appwrite.service` to share data across multiple components in an actual project easily.

Our `app.component.ts` should look like this:
```typescript
import { Component, OnInit } from '@angular/core';
import { Appwrite } from 'appwrite';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  title = 'appwrite-ssr';
  currencies: any; // Do not use any in real project

  async ngOnInit() {
    let sdk = new Appwrite();

    sdk
      .setEndpoint('https://server.matejbaco.eu/v1') // Your API Endpoint
      .setProject('60f2fb6e92712'); // Your project ID

    // Load currencies from appwrite
    const appwriteCurrencies = await sdk.locale.getCurrencies();

    // Store the result into component variable for use in HTML code
    this.currencies = appwriteCurrencies;
  }
}
```

First, we imported Appwrite SDK using `import { Appwrite } from 'appwrite';`. Then, inside `ngOnInit` we initialized a new instance of the SDK that is connected to our Appwrite server. Finally, we load a list of currencies from Appwrite and store it into a variable to use in HTML code.

Let's switch to `app.component.html`. This is our code:
```html
<h1>Total currencies:</h1>

<!-- We don't have data yet, loading... -->
<p *ngIf="!currencies">...</p>

<!-- Data loaded, let's count them -->
<p *ngIf="currencies">Total: {{ currencies.sum }}</p>

```

We simply write two blocks of code - one for when data is not loaded yet, one after the loading is finished. Now, if we run `ng serve` and visit `http://localhost:4200/`, we can see the currencies being loaded successfully:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wbpv4o3d5958kbeqs9xd.png)

What about server-side rendering? Let's see... If we look at the source code of our application, we can this:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rnfifhg3huofim8iuar0.png)


No useful data for bots! Let's fix that.

## Add Angular Universal to our project <a name="add-angular-universal-to-our-project"></a>

To prepare our project for server-side rendering, we need to add a new Angular library. Let's stop our Angular development server and run `ng add @nguniversal/express-engine`. Then, we can run `npm run dev:ssr` to have the same development server running, but this time with server-side rendering. Let's see what our website looks like to bots now:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98z6me4hxkyvv5jr08k6.png)

This is awesome, one step at a time! Our Angular code is being rendered properly because we can see our title `Total currencies:`. We are not done yet, because this pre-rendered HTML does not include our Appwrite data. Instead, we can see `...`. 

## Connect Appwrite to Angular Universal <a name="connect-appwrite-to-angular-universal"></a>

As mentioned initially, we will use a library that will help us run the task server-side. To do this, we stop our development server and run `npm i @bespunky/angular-zen`. Once the library is installed, let's start the development server with `npm run dev:ssr`.

Angular zen is an Angular library, so we need to add it into `imports` of our module for it to work properly. To do this, we go into `app.module.ts` and add add `RouterXModule` as an import. The module should look like this:
```typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterXModule } from '@bespunky/angular-zen/router-x';
import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule.withServerTransition({ appId: 'serverApp' }),
    AppRoutingModule,
    RouterXModule.forRoot(),
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

```

> My IDE noticed the error `Appears in the NgModule.imports of AppServerModule, but itself has errors`, but I ignored it, and it got resolved by Angular re-building the app. If you can see this error, simply restart the development server, and you should be good to go.

We need to use `RouteAware` class in our `app.component.ts` because we need to access its `resolveInMacroTask()` method. To do that, we can make our component extend `RouteAware`. Then we wrap our async code in `ngOnInit` into `resolveInMacroTask` and await its result as a promise. Our code will look like this:

```typescript
import { Component, OnInit } from '@angular/core';
import { RouteAware } from '@bespunky/angular-zen/router-x';
import { Appwrite } from 'appwrite';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent extends RouteAware implements OnInit {
  title = 'appwrite-ssr';
  currencies: any; // Do not use any in real project

  async ngOnInit() {
    let sdk = new Appwrite();

    sdk
      .setEndpoint('https://server.matejbaco.eu/v1') // Your API Endpoint
      .setProject('60f2fb6e92712'); // Your project ID

    await this.resolveInMacroTask(async () => {
      // Load currencies from appwrite
      const appwriteCurrencies = await sdk.locale.getCurrencies();

      // Store the result into component variable for use in HTML code
      this.currencies = appwriteCurrencies;
    }).toPromise();
  }
}
```

We are good to go! Let's see it in action. If I visit our page, I can see the data:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kvxn8o8le0a7hkweyqp.png)


If I look at the source code of pre-render, I can see the data too!

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1nppfft096pt5mqb44yq.png)

---

That's it! I hope this article helped you to use Appwrite with Angular Universal. If you have any questions, feel free to join Appwrite's Discord server and chat with their amazing community: https://appwrite.io/discord