Understanding Firebase Database

Firebase Database is a cloud-hosted, NoSQL data store. It enables real-time synchronization of data across users, making it well-suited for applications that demand live updates—think chat systems, collaborative editing tools, or social media streams.

Setting Up AngularFire

Begin by adding the @angular/fire package via npm in your Angular project:

npm install @angular/fire
Enter fullscreen mode Exit fullscreen mode

Next, bring in the required Firebase modules and configure the app. The snippet below handles initialization for both Firebase App and Firebase Database within your app.config.ts file.

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { initializeApp, provideFirebaseApp } from '@angular/fire/app';
import { provideRouter } from '@angular/router';
import { environment } from '../environments/environment';
import { routes } from './app.routes';
import { getDatabase, provideDatabase } from '@angular/fire/database';

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes),
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    provideDatabase(() => getDatabase()),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Make sure your Firebase configuration object is present in environment.firebaseConfig, located in both environment.ts and environment.prod.ts:

export const environment = {
    production: false,
    firebaseConfig: {
        apiKey: "-----",
        authDomain: "-----",
        projectId: "-----",
        storageBucket: "-----",
        messagingSenderId: "-----",
        appId: "-----",
    }
};
Enter fullscreen mode Exit fullscreen mode

With this configuration, Firebase is bootstrapped as soon as your Angular app launches.

Exploring app.component.ts

The code is deliberately kept in a single file so you can copy and paste it directly and get a working demo without creating multiple files. For production, it's advisable to split logic across different files.

import { CommonModule } from '@angular/common';
import { Component, inject } from '@angular/core';
import { FirebaseApp } from '@angular/fire/app';
import { getDatabase, objectVal, ref, set } from '@angular/fire/database';
import { Observable } from 'rxjs';

type Post = { caption: string; imageUrl: string };

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="feed-container">
      <button class="create-post-button" (click)="openCreatePost()">
        Create New Post
      </button>
      @for (post of posts$ | async | keyvalue; track post.key) {
      <div class="post-card">
        <img
          [src]="post.value.imageUrl"
          [alt]="post.value.caption"
          class="post-image"
        />
        <div class="post-footer">
          <h2>{{ post.value.caption }}</h2>
        </div>
      </div>
      }
    </div>
  `,
  styles: [
    `
      .feed-container {
        max-width: 600px;
        margin: 0 auto;
        padding: 20px;
      }
      .create-post-button {
        width: 100%;
        padding: 12px;
        background: #0095f6;
        color: white;
        border: none;
        border-radius: 4px;
        font-size: 16px;
        cursor: pointer;
        margin-bottom: 20px;
      }
      .create-post-button:hover {
        background: #0081d6;
      }
      .post-card {
        background: white;
        border: 1px solid #dbdbdb;
        border-radius: 3px;
        margin-bottom: 20px;
      }
      .post-image {
        width: 100%;
        height: auto;
      }
      .post-footer {
        padding: 16px;
      }
    `,
  ],
})
export class AppComponent {
  private DATABASE_TABLE_NAME = 'posts';
  private readonly database;

  readonly posts$: Observable<Record<string, Post>>;

  constructor() {
    this.database = getDatabase(inject(FirebaseApp));
    this.posts$ = objectVal(ref(this.database, this.DATABASE_TABLE_NAME));
  }

  openCreatePost() {
    const newPostKey = `${this.DATABASE_TABLE_NAME}/${this.getRandomNumber()}`;
    const newPostValue: Post = {
      caption: 'Angular 19 + Firebase Database Starter',
      imageUrl: 'https://images.unsplash.com/photo-1575936123452-b67c3203c357',
    };
    set(ref(this.database, newPostKey), newPostValue);
  }

  private getRandomNumber() {
    return Math.floor(Math.random() * (1000 - 1 + 1)) + 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

This component serves as the core of the application, pulling in essential pieces from Angular, @angular/fire, and RxJS.

  • getDatabase, objectVal, ref, and set are the primary Firebase Database utilities utilized here.
  • The Post type outlines the shape of a post object, comprising caption and imageUrl.
  • DATABASE_TABLE_NAME is a constant that specifies the database table for posts, set to 'posts'.
  • database represents the Firebase Database instance, created via getDatabase(inject(FirebaseApp)).
  • posts$ is an Observable providing a live stream of data from Firebase.
  • objectVal(ref(this.database, this.DATABASE_TABLE_NAME)) sets up a reference to the 'posts' table and transforms it into an Observable using objectVal, which emits the entire object stored at that PATH.

Retrieving Data

The posts$ Observable fetches and refreshes data from the 'posts' table automatically. Any modification in the database triggers a new emission, and the Angular template reflects the change instantly.

Saving Data

The openCreatePost method illustrates how to persist data to Firebase. It leverages the set function to store data at a designated database location.

Taking the App Further

Several opportunities exist to build upon this foundational starter project:

  • UI Enhancements: Improve the visual design with better styling, add loading states, and implement error messaging.
  • User Input Forms: Introduce a form that allows users to compose and submit posts, leveraging Angular Forms for validation and two-way data binding.
  • Data Structuring: Plan your data model thoughtfully, aiming for efficiency and scalability. As a NoSQL store, Firebase requires you to design your JSON structure with foresight.
  • Security Configuration: Establish Firebase Database security rules to block unauthorized access and safeguard your data.
  • Error Management: Put in place comprehensive error handling to manage failures gracefully.

This combination of Angular and Firebase Database offers a strong starting point for crafting interactive, real-time web experiences. By mastering the core concepts and extending the initial functionality, you can build compelling and responsive user interfaces.


🎉 It's remarkable how straightforward this functionality is to implement, and I'm genuinely thrilled about it. 😄

Feel free to connect with me on GitHub, where I'm developing interesting projects.

Thank you for reading—don't forget to leave a ❤️.
Bye for now 👋