← Back to all tutorials

Firebase API

Create a service to interact with Firebase Realtime Database — full CRUD operations via the REST API.

Firebase API

Now that we're connected to Firebase, let's build a service that handles all CRUD operations (Create, Read, Update, Delete) using Firebase's REST API.

Firebase REST Endpoints

OperationHTTP MethodEndpoint
CreatePOST/collection.json
Read allGET/collection.json
Read oneGET/collection/id.json
UpdatePUT / PATCH/collection/id.json
DeleteDELETE/collection/id.json

Data Service

// data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';

interface Post {
    id?: string;
    title: string;
    content: string;
    author: string;
}

@Injectable({ providedIn: 'root' })
export class DataService {
    private dbUrl = environment.firebase.databaseURL;

    constructor(private http: HttpClient) {}

    // CREATE — POST adds a new item with an auto-generated key
    createPost(post: Post): Observable<any> {
        return this.http.post(`${this.dbUrl}/posts.json`, post);
    }

    // READ ALL — GET returns an object of objects
    getPosts(): Observable<Post[]> {
        return this.http.get<{ [key: string]: Post }>(`${this.dbUrl}/posts.json`).pipe(
            map(data => {
                if (!data) return [];
                return Object.keys(data).map(key => ({
                    ...data[key],
                    id: key  // Firebase key becomes our ID
                }));
            })
        );
    }

    // READ ONE
    getPost(id: string): Observable<Post> {
        return this.http.get<Post>(`${this.dbUrl}/posts/${id}.json`);
    }

    // UPDATE — PUT replaces the entire object
    updatePost(id: string, post: Post): Observable<any> {
        return this.http.put(`${this.dbUrl}/posts/${id}.json`, post);
    }

    // DELETE
    deletePost(id: string): Observable<any> {
        return this.http.delete(`${this.dbUrl}/posts/${id}.json`);
    }
}

Understanding Firebase JSON Structure

// Firebase stores data as a JSON tree:
{
    "posts": {
        "-NxAbC123": {
            "title": "First Post",
            "content": "Hello, Firebase!",
            "author": "Alice"
        },
        "-NxDeF456": {
            "title": "Second Post",
            "content": "Learning Angular",
            "author": "Bob"
        }
    }
}

// GET /posts.json returns the object above
// POST /posts.json auto-generates a key like "-NxAbC123"
// The response from POST: { "name": "-NxGhI789" }

The map() Operator

Firebase returns data as an object of objects, but we need an array for *ngFor:

// Firebase returns:
{ "-NxAbC123": { title: "Post 1" }, "-NxDeF456": { title: "Post 2" } }

// We transform it to:
[
    { id: "-NxAbC123", title: "Post 1" },
    { id: "-NxDeF456", title: "Post 2" }
]

Using the Service

// post-list.component.ts
export class PostListComponent implements OnInit {
    posts: Post[] = [];

    constructor(private dataService: DataService) {}

    ngOnInit(): void {
        this.loadPosts();
    }

    loadPosts(): void {
        this.dataService.getPosts().subscribe(posts => {
            this.posts = posts;
        });
    }

    onDelete(id: string): void {
        this.dataService.deletePost(id).subscribe(() => {
            this.posts = this.posts.filter(p => p.id !== id);
        });
    }
}

PUT vs PATCH

MethodBehavior
PUTReplaces the entire object at that path
PATCHUpdates only the specified fields
// PUT — replaces everything
this.http.put(`${url}/posts/${id}.json`, { title: "New Title" });
// Result: { title: "New Title" } ← content and author DELETED!

// PATCH — updates only specified fields
this.http.patch(`${url}/posts/${id}.json`, { title: "New Title" });
// Result: { title: "New Title", content: "original", author: "Alice" } ← safe!

Key Takeaways

  • Firebase REST API uses standard HTTP methods + .json extension
  • POST creates with auto-generated keys; GET returns object-of-objects
  • Use RxJS map() to transform Firebase responses into arrays
  • Use PATCH (not PUT) for partial updates to avoid data loss
  • Keep all HTTP logic in the service — components just subscribe