🅰️Framework Integration

Integrating MeshBase with Angular

Build enterprise-grade Angular applications powered by MeshBase. Services, RxJS, HttpClient, and Angular Universal—all covered.

Why MeshBase + Angular?

🏢Enterprise Ready

Angular + MeshBase = enterprise-grade content management. TypeScript-first, testable, scalable architecture.

🔄RxJS Integration

MeshBase API calls return Observables. Perfect for Angular's reactive patterns and async pipes.

🎯Type Safety

Full TypeScript support with interfaces and models. Catch errors at compile time, not runtime.

🚀Angular Universal

Server-side rendering with Angular Universal works seamlessly with MeshBase for SEO and performance.

What You'll Need

  • âś“Angular 15+ project
  • âś“A MeshBase account with content types defined
  • âś“Your MeshBase public API key

Quick Start

Set up MeshBase in your Angular app with a service.

1. Configure environment

Update src/environments/environment.ts:

export const environment = {
  production: false,
  meshbase: {
    apiUrl: 'https://api.meshbase.io/v1',
    apiKey: 'your-api-key-here'
  }
};

2. Define models

Create src/app/models/blog-post.model.ts:

export interface BlogPost {
  id: string;
  title: string;
  excerpt: string;
  content: string;
  coverImage?: string;
  publishedAt: string;
}

export interface MeshBaseResponse<T> {
  data: T[];
  meta?: {
    total: number;
    page: number;
  };
}

3. Create content service

Generate and update src/app/services/content.service.ts:

ng generate service services/content
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { environment } from '../../environments/environment';
import { BlogPost, MeshBaseResponse } from '../models/blog-post.model';

@Injectable({
  providedIn: 'root'
})
export class ContentService {
  private apiUrl = environment.meshbase.apiUrl;
  private apiKey = environment.meshbase.apiKey;

  private get headers(): HttpHeaders {
    return new HttpHeaders({
      'Authorization': `Bearer ${this.apiKey}`,
      'Content-Type': 'application/json'
    });
  }

  constructor(private http: HttpClient) {}

  getBlogPosts(): Observable<BlogPost[]> {
    return this.http.get<MeshBaseResponse<BlogPost>>(
      `${this.apiUrl}/blog-posts`,
      { headers: this.headers }
    ).pipe(
      map(response => response.data)
    );
  }

  getBlogPost(id: string): Observable<BlogPost> {
    return this.http.get<BlogPost>(
      `${this.apiUrl}/blog-posts/${id}`,
      { headers: this.headers }
    );
  }
}

4. Use in components

Create src/app/blog/blog-list.component.ts:

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { ContentService } from '../services/content.service';
import { BlogPost } from '../models/blog-post.model';

@Component({
  selector: 'app-blog-list',
  template: `
    <div class="blog-list">
      <h1>Blog Posts</h1>
      
      <div *ngIf="posts$ | async as posts; else loading">
        <article *ngFor="let post of posts">
          <h2>
            <a [routerLink]="['/blog', post.id]">
              {{ post.title }}
            </a>
          </h2>
          <p>{{ post.excerpt }}</p>
        </article>
      </div>
      
      <ng-template #loading>
        <p>Loading posts...</p>
      </ng-template>
    </div>
  `
})
export class BlogListComponent implements OnInit {
  posts$!: Observable<BlogPost[]>;

  constructor(private contentService: ContentService) {}

  ngOnInit(): void {
    this.posts$ = this.contentService.getBlogPosts();
  }
}
✨

You're Done!

Your Angular app now fetches MeshBase content. The async pipe handles subscriptions automatically—clean and reactive!

State Management with NgRx

For complex apps, integrate MeshBase with NgRx.

Install NgRx

ng add @ngrx/store @ngrx/effects

Create actions

// store/blog/blog.actions.ts
import { createAction, props } from '@ngrx/store';
import { BlogPost } from '../../models/blog-post.model';

export const loadPosts = createAction('[Blog] Load Posts');
export const loadPostsSuccess = createAction(
  '[Blog] Load Posts Success',
  props<{ posts: BlogPost[] }>()
);
export const loadPostsFailure = createAction(
  '[Blog] Load Posts Failure',
  props<{ error: string }>()
);

Create effects

// store/blog/blog.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { map, catchError, switchMap } from 'rxjs/operators';
import { ContentService } from '../../services/content.service';
import * as BlogActions from './blog.actions';

@Injectable()
export class BlogEffects {
  loadPosts$ = createEffect(() =>
    this.actions$.pipe(
      ofType(BlogActions.loadPosts),
      switchMap(() =>
        this.contentService.getBlogPosts().pipe(
          map(posts => BlogActions.loadPostsSuccess({ posts })),
          catchError(error => of(BlogActions.loadPostsFailure({ error: error.message })))
        )
      )
    )
  );

  constructor(
    private actions$: Actions,
    private contentService: ContentService
  ) {}
}

Server-Side Rendering (Angular Universal)

Pre-render pages with Angular Universal for better SEO and initial load performance.

Add Angular Universal

ng add @nguniversal/express-engine

Transfer state

Avoid fetching data twice (server + client):

import { TransferState, makeStateKey } from '@angular/platform-browser';

const POSTS_KEY = makeStateKey<BlogPost[]>('posts');

@Injectable({ providedIn: 'root' })
export class ContentService {
  constructor(
    private http: HttpClient,
    private transferState: TransferState
  ) {}

  getBlogPosts(): Observable<BlogPost[]> {
    // Try to get from transfer state first
    const cachedPosts = this.transferState.get(POSTS_KEY, null);
    
    if (cachedPosts) {
      this.transferState.remove(POSTS_KEY);
      return of(cachedPosts);
    }

    // Fetch from API
    return this.http.get<MeshBaseResponse<BlogPost>>(
      `${this.apiUrl}/blog-posts`,
      { headers: this.headers }
    ).pipe(
      map(response => response.data),
      tap(posts => this.transferState.set(POSTS_KEY, posts))
    );
  }
}

HTTP Interceptors

Add authentication headers globally with an interceptor.

Create interceptor

// interceptors/meshbase.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { environment } from '../../environments/environment';

@Injectable()
export class MeshbaseInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    // Only add auth to MeshBase requests
    if (req.url.includes(environment.meshbase.apiUrl)) {
      const authReq = req.clone({
        setHeaders: {
          Authorization: `Bearer ${environment.meshbase.apiKey}`
        }
      });
      return next.handle(authReq);
    }
    
    return next.handle(req);
  }
}

Register in app module

// app.module.ts
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { MeshbaseInterceptor } from './interceptors/meshbase.interceptor';

@NgModule({
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MeshbaseInterceptor,
      multi: true
    }
  ]
})
đź”’

Cleaner Service Code

With interceptors, your service doesn't need to handle auth headers manually. Cleaner code, centralized authentication!

Next Steps