Documentation Index
Fetch the complete documentation index at: https://mintlify.com/amark/gun/llms.txt
Use this file to discover all available pages before exploring further.
Angular Integration
GUN integrates well with Angular through services and can be combined with RxJS observables for reactive data patterns.
Installation
Install GUN
Install GUN in your Angular project: Install type definitions
Install TypeScript definitions (optional but recommended):npm install @types/gun --save-dev
Create GUN service
Generate a service to manage your GUN instance:
Creating a GUN Service
Create a service to provide a singleton GUN instance throughout your app:
// gun.service.ts
import { NgModule, Injectable } from '@angular/core';
import Gun from 'gun/gun';
@Injectable()
export class GunDb {
readonly gun = Gun(location.origin + '/gun');
}
Register the service in your app module:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { GunDb } from './gun.service';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [GunDb],
bootstrap: [AppComponent]
})
export class AppModule { }
RxJS Observable Helpers
Create helper functions to convert GUN’s callbacks into RxJS observables:
// gun.helper.ts
import { Observable } from 'rxjs/Observable';
import Gun from 'gun/gun';
import { pick } from 'underscore';
export function on$(node, cleanup = true): Observable<any> {
return Observable.fromEventPattern(
h => {
// there is no way to off() an on() until at least one value is triggered
// so that we can access the event listener to off() it
const signal = { stop: false };
node.on((data, key, at, ev) => {
if (signal.stop) {
ev.off();
} else {
// modifying data directly does not seem to work...
h(cleanup ? pick(data, (v, k, o) => v !== null && k !== '_') : data);
}
});
return signal;
},
(h, signal) => { signal.stop = true; }
);
}
export function val$(node): Observable<any> {
return new Observable(o => node.val(v => {
o.next(v);
o.complete();
}));
}
Using GUN in Components
Here’s a complete example of a todo component using GUN:
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import Gun from 'gun/gun';
import { GunDb } from 'app/gun.service';
import { on$ } from 'app/gun.helper';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
newTodo = '';
todos = this.db.gun.get('todos');
todos$: Observable<string[]> = on$(this.todos);
todosSub: Subscription;
constructor(private db: GunDb) { }
ngOnInit() { }
add() {
if (this.newTodo) {
this.todos.get(Gun.text.random()).put(this.newTodo);
this.newTodo = '';
}
}
delete(key: string) {
this.todos.get(key).put(null);
}
sub() {
this.todosSub = this.todos$.subscribe(v => console.log(v));
}
unsub() {
this.todosSub.unsubscribe();
}
}
Component Template
<!-- app.component.html -->
<div class="todo-app">
<h1>GUN Todo App</h1>
<div class="add-todo">
<input
type="text"
[(ngModel)]="newTodo"
(keyup.enter)="add()"
placeholder="Add a todo..."
>
<button (click)="add()">Add</button>
</div>
<ul class="todo-list">
<li *ngFor="let todo of todos$ | async | keyvalue"
(click)="delete(todo.key)">
{{ todo.value }}
</li>
</ul>
</div>
Advanced RxJS Integration
Create a more sophisticated service with RxJS operators:
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { map, filter } from 'rxjs/operators';
import Gun from 'gun/gun';
@Injectable({
providedIn: 'root'
})
export class GunService {
private gun = Gun(location.origin + '/gun');
// Convert GUN node to Observable
observe(path: string): Observable<any> {
return new Observable(observer => {
this.gun.get(path).on((data, key) => {
observer.next(data);
});
}).pipe(
filter(data => data !== null && data !== undefined)
);
}
// Get data once
once(path: string): Observable<any> {
return new Observable(observer => {
this.gun.get(path).once((data, key) => {
observer.next(data);
observer.complete();
});
});
}
// Put data
put(path: string, data: any): void {
this.gun.get(path).put(data);
}
// Get gun instance for direct access
getGun() {
return this.gun;
}
}
Usage in a component:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { GunService } from './gun.service';
@Component({
selector: 'app-data',
template: `
<div *ngIf="data$ | async as data">
<h2>{{ data.title }}</h2>
<p>{{ data.content }}</p>
</div>
`
})
export class DataComponent implements OnInit {
data$: Observable<any>;
constructor(private gunService: GunService) {}
ngOnInit() {
this.data$ = this.gunService.observe('myData');
}
updateData(title: string, content: string) {
this.gunService.put('myData', { title, content });
}
}
User Authentication
Implement user authentication with GUN’s SEA:
import { Injectable } from '@angular/core';
import Gun from 'gun/gun';
import 'gun/sea';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private gun = Gun();
private user = this.gun.user();
signup(username: string, password: string): Promise<any> {
return new Promise((resolve, reject) => {
this.user.create(username, password, (ack) => {
if (ack.err) {
reject(ack.err);
} else {
resolve(ack);
}
});
});
}
login(username: string, password: string): Promise<any> {
return new Promise((resolve, reject) => {
this.user.auth(username, password, (ack) => {
if (ack.err) {
reject(ack.err);
} else {
resolve(ack);
}
});
});
}
logout(): void {
this.user.leave();
}
isAuthenticated(): boolean {
return this.user.is !== undefined;
}
getUser() {
return this.user;
}
}
TypeScript Configuration
Ensure your tsconfig.json includes proper settings:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
Best Practices
- Use services: Encapsulate GUN logic in Angular services
- RxJS integration: Convert GUN callbacks to observables for Angular’s async pipe
- Dependency injection: Provide GUN services at the module or component level
- Type safety: Use TypeScript interfaces for your data structures
- Cleanup subscriptions: Always unsubscribe in
ngOnDestroy()
Common Patterns
Cleanup on Destroy
export class MyComponent implements OnInit, OnDestroy {
private subscription: Subscription;
ngOnInit() {
this.subscription = this.gunService.observe('data')
.subscribe(data => console.log(data));
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Using Async Pipe
export class MyComponent {
data$ = this.gunService.observe('myData');
constructor(private gunService: GunService) {}
}
<div *ngIf="data$ | async as data">
{{ data | json }}
</div>
Next Steps