3
\$\begingroup\$

I'm wondering if there's a better way to write the ApiService and the CategoryService so that CategoryService is basically just a call to extend ApiService with the string 'category' and the base type 'ICategory' or if this is the best that I can get from typescript and angular? There will be many more endpoint services in the final app

Any other general comments on code style etc are also very welcome (or if anyone knows how to tell Webstorm to not put the constructor closing braces on the next line!!)

ApiService

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';

const baseUrl = environment.serverBaseUrl;
const apiEndpoint = environment.apiEndpoint || '/api/v1/';
const apiUrl = `${baseUrl}${apiEndpoint}`;

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  constructor(private http: HttpClient) {
  }

  private createTokenOptions() {
    return {headers: new HttpHeaders().set('Authorization', `Bearer ${localStorage.getItem('access_token')}`)};
  }

  get<T>(serviceEndpoint: string, id?: number): Observable<T> {
    let url = `${apiUrl}${serviceEndpoint}`;
    if (id) {
      url += `/${id}`;
    }
    return this.http.get<T>(url, this.createTokenOptions());
  }

  create<T>(serviceEndpoint, item: T): Observable<object> {
    const options = this.createTokenOptions();
    options.headers = options.headers.append('Content-Type', 'application/json');
    return this.http.post(`${apiUrl}${serviceEndpoint}`, JSON.stringify(item), options);
  }

  delete<T>(serviceEndpoint: string, id: number): Observable<T> {
    return this.http.delete<T>(`${apiUrl}${serviceEndpoint}/${id}`, this.createTokenOptions());
  }
}

CategoryService

import { Injectable } from '@angular/core';
import { ApiService } from './api.service';
import { Observable } from 'rxjs';
import { ICategory } from '../interfaces/category.model';


@Injectable({
  providedIn: 'root'
})
export class CategoryService {

  private serviceEndpoint = 'category';

  constructor(private api: ApiService) {
  }

  getCategories() {
    return this.api.get<ICategory[]>(this.serviceEndpoint);
  }

  getCategory(id: number): Observable<ICategory> {
    return this.api.get<ICategory>(this.serviceEndpoint, id);
  }

  createCategory(category: ICategory) {
    return this.api.create<ICategory>(this.serviceEndpoint, category);
  }

  deleteCategory(id: number) {
    return this.api.delete<ICategory>(this.serviceEndpoint, id);
  }
}
\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

I think you can use HTTP Interceptors to simplify the code.

For the authorization

@Injectable()
export class AuthorizationInterceptor implements HttpInterceptor {

  constructor() {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    // skip requests for local assets
    if (request.url.indexOf("assets/") >= 0) {
      return next.handle(request);
    }

    const apiReq = request.clone({
      url: request.url,
      headers: request.headers.set('Authorization', `Bearer ${localStorage.getItem('access_token')}`)
    });
    return next.handle(apiReq);
  }
}

For the base url

Something along the line

@Injectable()
export class BaseUrlInterceptor implements HttpInterceptor {

  constructor(
    @Inject("BASE_API_URL") private baseUrl: string) {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    // skip requests for local assets
    // maybe put extra condition to allow for multiple API usage
    if (request.url.indexOf("assets/") >= 0) {
      return next.handle(request);
    }

    const apiReq = request.clone({
      url: `${this.baseUrl}${request.url}`
    });
    return next.handle(apiReq);
  }
}

Of course, the module should define the interceptors:

  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthorizationInterceptor,
      multi: true
    }
  ]
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks great suggestion for the interceptors. I went with just checking if the url has the baseurl for my rest service in there and to add the bearer token if so. I did keep the base api and the various rest endpoints as I think there's a lot to be said for it being easy to follow, I would love a way to do something like a spring jpa repo in java where I could just declare class CategoryService extends ApiService<'category', Category> \$\endgroup\$
    – LiamRyan
    Sep 1, 2019 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.