Appwrite is an open-source backend-as-a-service that gives builders with a core set of performance wanted to construct any utility with any stack. From database interactions to authentication, real-time updates, and extra.
When constructing internet purposes with Angular, it is common follow to hook up with completely different APIs to handle knowledge, authenticate customers, and probably hearken to dwell updates to knowledge. The APIs to hook up with these completely different providers could possibly be performed via a number of suppliers. With Appwrite, you are able to do all of these items utilizing a single backend. This submit exhibits you the right way to stand up and working with Appwrite, authenticate customers, handle knowledge, and hearken to realtime occasions utilizing a chat utility.
Stipulations
To get began with Appwrite, it’s worthwhile to have Docker put in in your native machine or server. After you’ve Docker working, use the next command to put in and run Appwrite.
docker run -it --rm
--volume /var/run/docker.sock:/var/run/docker.sock
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw
--entrypoint="set up"
appwrite/appwrite:0.15.0
Additionally take a look at the entire set up information for extra details about the method. If the whole lot went easily, you possibly can go to the Appwrite Console and register your root account.
Subsequent, let’s arrange the primary undertaking.
Making a Venture
You possibly can host many alternative purposes in Appwrite utilizing initiatives. To create a undertaking:
- Click on on Create Venture
- Click on on the pencil icon and enter ngchat because the customized Venture ID
- Enter Angular Chat because the identify
- Click on Create
Subsequent, let’s setup the database and assortment for the chat utility.
Making a Database and Assortment
A database in Appwrite is group of collections for managing knowledge. To create a database, go to the Database part:
- Click on on Create Database
- Enter chat because the customized Database ID
- Enter Chat because the identify
- Click on Create
For the gathering:
- Click on on Create Assortment
- Enter messages because the customized Assortment ID
- Enter Chat Messages because the identify
- Click on Create
We additionally wish to configure permissions for the gathering for learn/write entry. For messages, you’ll select Doc Degree permissions. You possibly can select extra granular permissions relying in your use case. The permissions web page has extra particulars on permissions so the consumer retains possession of their message.
Creating Assortment Attributes
Every assortment in an Appwrite database consists of attributes that mannequin the construction for the doc you wish to retailer. For the chat utility, you’ll retailer the consumer’s identify and message.
Creating Doc Attributes
Attributes may be outlined as strings, numbers, emails, and extra. To create an attribute:
- Click on on Create Attribute.
- Choose the kind of Attribute to create.
Use the desk beneath to create the mandatory attributes for chat.
key | dimension | required | array |
---|---|---|---|
consumer | 32 | true | false |
message | 10000 | true | false |
When a doc is created within the assortment, it additionally has additional metadata for the when the doc is created and up to date, named $createdAt
and $updatedAt
respectively. You should utilize this metadata for querying, syncing, and different use circumstances.
You are able to do different issues like toggle providers, select which OAuth supplier to make use of and extra, however for this chat utility, nameless authentication is used, which can be enabled by default.
Subsequent, let’s put the Angular utility collectively.
Constructing with Angular
To start out, clone an current repository already working Angular model 14 with a few routes setup for login and chat. Use the command beneath to clone the GitHub repository.
git clone git@github.com:brandonroberts/appwrite-angular-chat.git
Set up the dependencies:
yarn
And begin the appliance to get the event server working
yarn begin
Navigate to http://localhost:4200 within the browser to view the login web page.
Organising the Appwrite Config
To configure Appwrite in our Angular undertaking, configure some surroundings variables first for the Appwrite endpoint, undertaking, and assortment values.
Replace the src/environments/surroundings.ts
export const surroundings = {
endpoint: 'http://localhost/v1',
projectId: 'ngchat',
databaseId: 'chat',
chatCollectionId: 'messages',
manufacturing: false
};
After the surroundings variables are set, transfer on to establishing the Appwrite Net SDK.
To initialize the Appwrite Net SDK, use the appwrite bundle put in earlier, together with establishing some Injection Tokens in Angular to have the ability to inject the SDK into providers created later.
Let’s create 2 tokens, one for the Appwrite Surroundings variables, and one for the SDK occasion itself.
Create a brand new file named src/appwrite.ts
and configure the two tokens as root suppliers.
import { inject, InjectionToken } from '@angular/core';
import {
Account,
Consumer as Appwrite,
Databases
} from 'appwrite';
import { surroundings } from 'src/environments/surroundings';
interface AppwriteConfig {
endpoint: string;
projectId: string;
databaseId: string;
chatCollectionId: string;
}
export const AppwriteEnvironment = new InjectionToken<AppwriteConfig>(
'Appwrite Config',
{
providedIn: 'root',
manufacturing unit() {
const { endpoint, projectId, databaseId, chatCollectionId } = surroundings;
return {
endpoint,
databaseId,
projectId,
chatCollectionId,
};
},
}
);
The primary token units up the surroundings variables to allow them to be injected to a number of providers.
export const AppwriteApi = new InjectionToken<{
database: Databases;
account: Account;
}>('Appwrite SDK', {
providedIn: 'root',
manufacturing unit() {
const env = inject(AppwriteEnvironment);
const appwrite = new Appwrite();
appwrite.setEndpoint(env.endpoint);
appwrite.setProject(env.projectId);
const database = new Databases(appwrite, env.databaseId);
const account = new Account(appwrite);
return { database, account };
},
});
The second token creates an occasion of the Appwrite Net SDK, units the endpoint to level to the working Appwrite occasion, and the undertaking ID configured earlier.
After the Appwrite SDK is setup, let’s create some providers for authentication and accessing chat messages.
First, let’s create an src/auth.service.ts that lets you login, examine auth standing, and logout
import { inject, Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { Fashions } from 'appwrite';
import {
BehaviorSubject,
concatMap,
from,
faucet,
mergeMap
} from 'rxjs';
import { AppwriteApi } from './appwrite';
@Injectable({
providedIn: 'root',
})
export class AuthService {
personal appwriteAPI = inject(AppwriteApi);
personal _user = new BehaviorSubject<Fashions.Consumer<Fashions.Preferences> | null>(
null
);
readonly consumer$ = this._user.asObservable();
constructor(personal router: Router) {}
login(identify: string) {
const authReq = this.appwriteAPI.account.createAnonymousSession();
return from(authReq).pipe(
mergeMap(() => this.appwriteAPI.account.updateName(identify)),
concatMap(() => this.appwriteAPI.account.get()),
faucet((consumer) => this._user.subsequent(consumer))
);
}
async isLoggedIn() {
strive {
const consumer = await this.appwriteAPI.account.get();
this._user.subsequent(consumer);
return true;
} catch (e) {
return false;
}
}
async logout() {
strive {
await this.appwriteAPI.account.deleteSession('present');
} catch (e) {
console.log(`${e}`);
} lastly {
this.router.navigate(['/']);
this._user.subsequent(null);
}
}
}
The AuthService injects the Appwrite SDK to:
- Authenticate the consumer with the login technique, replace the identify, and retailer the present consumer in an observable.
- Checks to see if the consumer is logged in and returns a boolean
- Logs the consumer out by clearing the present session
With the Appwrite SDK, all of that is performed with out utilizing Angular’s HttpClient service. You possibly can at all times entry Appwrite’s REST APIs instantly, nevertheless it’s not required because the SDK handles this for you.
Subsequent, let’s create the src/chat.service.ts to load and ship chat messages.
import { inject, Injectable } from '@angular/core';
import { Fashions, RealtimeResponseEvent } from 'appwrite';
import { BehaviorSubject, take, concatMap, filter } from 'rxjs';
import { AppwriteApi, AppwriteEnvironment } from './appwrite';
import { AuthService } from './auth.service';
export kind Message = Fashions.Doc & {
consumer: string;
message: string;
};
@Injectable({
providedIn: 'root',
})
export class ChatService {
personal appwriteAPI = inject(AppwriteApi);
personal appwriteEnvironment = inject(AppwriteEnvironment);
personal _messages$ = new BehaviorSubject<Message[]>([]);
readonly messages$ = this._messages$.asObservable();
constructor(personal authService: AuthService) {}
loadMessages() {
this.appwriteAPI.database
.listDocuments<Message>(
this.appwriteEnvironment.chatCollectionId,
[],
100,
0,
undefined,
undefined,
[],
['ASC']
)
.then((response) => {
this._messages$.subsequent(response.paperwork);
});
}
sendMessage(message: string) {
return this.authService.consumer$.pipe(
filter((consumer) => !!consumer),
take(1),
concatMap((consumer) => {
const knowledge = {
consumer: consumer!.identify,
message,
};
return this.appwriteAPI.database.createDocument(this.appwriteEnvironment.chatCollectionId,
'distinctive()',
knowledge,
['role:all'],
[`user:${user!.$id}`]
);
})
);
}
}
The ChatService:
- Injects the Appwrite Surroundings variables
- Units up an observable of chat messages
- Makes use of the Appwrite SDK to load chat messages from the messages assortment
- Will get the at the moment logged in consumer so as to add chat messages to the messages assortment.
- Assigns permissions to the doc so anybody can learn, however solely the particular consumer can replace/delete.
With the providers arrange, we are able to transfer on to the parts for login and chat.
Constructing the Login web page
For the login part, use the AuthService to login utilizing nameless authentication with the offered identify.
import { Element } from '@angular/core';
import {
FormControl,
FormGroup,
ReactiveFormsModule
} from '@angular/varieties';
import { Router } from '@angular/router';
import { faucet } from 'rxjs';
import { AuthService } from './auth.service';
@Element({
selector: 'app-login',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<div class="app-container">
<div class="content material">
<span class="appwrite-chat">Angular Chat</span>
<div class="login-container">
<kind [formGroup]="kind" class="login-form" (ngSubmit)="login()">
<p class="login-name">
<label for="identify">Identify</label>
<enter
kind="textual content"
id="identify"
formControlName="identify"
placeholder="Enter Identify"
/>
</p>
<button kind="submit">Begin Chatting</button>
</kind>
</div>
</div>
</div>
`
})
export class LoginComponent {
kind = new FormGroup({
identify: new FormControl('', { nonNullable: true }),
});
constructor(
personal authService: AuthService,
personal router: Router
) {}
login() {
const identify = this.kind.controls.identify.worth;
this.authService
.login(identify)
.pipe(
faucet(() => {
this.router.navigate(['/chat']);
})
)
.subscribe();
}
}
After the authentication is profitable, we redirect to the chat web page.
Displaying Chat Messages
With the Chat part, begin with displaying chat messages utilizing the ChatService:
import { CommonModule } from '@angular/widespread';
import { Element, OnInit } from '@angular/core';
import {
FormControl,
FormGroup,
ReactiveFormsModule
} from '@angular/varieties';
import { faucet } from 'rxjs';
import { ChatService } from './chat.service';
import { AuthService } from './auth.service';
@Element({
selector: 'app-chat',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<div class="chat-container" *ngIf="consumer$ | async as vm; else loading">
<div class="chat-header">
<div class="title">Let's Chat</div>
<div class="depart" (click on)="logout()">Depart Room</div>
</div>
<div class="chat-body">
<div
id="{{ message.$id }}"
*ngFor="let message of messages$ | async"
class="message"
>
<span class="identify">{{ message.consumer }}:</span>
{{ message.message }}
</div>
</div>
<div class="chat-message">
<kind [formGroup]="kind" (ngSubmit)="sendMessage()">
<enter
kind="textual content"
formControlName="message"
placeholder="Kind a message..."
/>
<button kind="submit" class="send-message">
<svg
class="arrow"
width="24"
top="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M13.0737 3.06325C12.8704 2.65671 12.4549 2.3999 12.0004 2.3999C11.5459 2.3999 11.1304 2.65671 10.9271 3.06325L2.52709 19.8632C2.31427 20.2889 2.37308 20.8001 2.67699 21.1663C2.98091 21.5325 3.4725 21.6845 3.93007 21.5537L9.93006 19.8395C10.4452 19.6923 10.8004 19.2214 10.8004 18.6856V13.1999C10.8004 12.5372 11.3376 11.9999 12.0004 11.9999C12.6631 11.9999 13.2004 12.5372 13.2004 13.1999V18.6856C13.2004 19.2214 13.5556 19.6923 14.0707 19.8394L20.0707 21.5537C20.5283 21.6845 21.0199 21.5325 21.3238 21.1663C21.6277 20.8001 21.6865 20.2889 21.4737 19.8632L13.0737 3.06325Z"
fill="#373B4D"
/>
</svg>
</button>
</kind>
</div>
</div>
<ng-template #loading>Loading...</ng-template>
`,
types: [...]
})
export class ChatComponent implements OnInit {
kind = new FormGroup({
message: new FormControl('', { nonNullable: true }),
});
consumer$ = this.authService.consumer$;
messages$ = this.chatService.messages$;
constructor(
personal authService: AuthService,
personal chatService: ChatService
) {}
ngOnInit() {
this.chatService.loadMessages();
}
sendMessage() {
const message = this.kind.controls.message.worth;
this.chatService
.sendMessage(message)
.pipe(
faucet(() => {
this.kind.reset();
})
)
.subscribe();
}
async logout() {
await this.authService.logout();
}
}
The ChatComponent makes use of the AuthService and ChatService to:
- Use the present logged in consumer
- Hearken to the observable of chat messages
- Load the chat messages within the ngOnInit of the part
- Use the enter discipline to ship the message utilizing the ChatService
- Logout from the chat web page
We’re capable of load chat messages, however let’s add the fascinating half and combine some realtime chat messages.
Connecting to Realtime Occasions
Appwrite offers realtime updates from virtually each occasion that occurs within the Appwrite system, reminiscent of database information being inserted, up to date or deleted. These occasions are offered via a WebSocket. To subscribe to realtime, replace the ChatService with a listenToMessages technique to subscribe to occasions from the messages assortment.
export class ChatService {
...
listenToMessages() {
return this.appwriteAPI.database.consumer.subscribe(
`databases.chat.collections.messages.paperwork`,
(res: RealtimeResponseEvent<Message>) => {
if (res.occasions.consists of('databases.chat.collections.messages.paperwork.*.create')) {
const messages: Message[] = [...this._messages$.value, res.payload];
this._messages$.subsequent(messages);
}
}
);
}
}
Every time a brand new message is created, the brand new message is pushed into the observable of customers so we’ve realtime updates wired up. To start out listening to realtime occasions:
- Replace the ngOnInit of the ChatComponent to name the strategy.
- Retailer the dwell connection for unsubscribing
- Destroy the dwell connection when the part is destroyed
export class ChatComponent implements OnInit, OnDestroy {
messageunSubscribe!: () => void;
kind = new FormGroup({
message: new FormControl('', { nonNullable: true }),
});
consumer$ = this.authService.consumer$;
messages$ = this.chatService.messages$;
constructor(
personal authService: AuthService,
personal chatService: ChatService
) {}
ngOnInit() {
this.chatService.loadMessages();
this.messageunSubscribe = this.chatService.listenToMessages();
}
ngOnDestroy() {
this.messageunSubscribe();
}
}
Abstract
And that’s it! We now have a functioning Angular utility with:
- Authentication
- Database administration
- Realtime occasions
There’s extra we might do right here, however as you possibly can see, you possibly can construct absolutely anything with the core performance already taken care of. And cloud capabilities provide help to prolong the performance of Appwrite even additional.
To view the working instance:
https://appwrite-angular-chat.netlify.app
GitHub Repo: https://github.com/brandonroberts/appwrite-angular-chat
Be taught Extra
🚀 Getting Began Tutorial
🚀 Appwrite GitHub
📜 Appwrite Docs
💬 Discord Group
Should you favored this, click on the ❤️ so different folks will see it. Comply with Brandon Roberts and Appwrite on Twitter for extra updates!