Akita: EntityService Feature

Created on 16 Jul 2019  路  6Comments  路  Source: datorama/akita

EntityService Feature

We're working on a new service that will simplify the work with REST APIs in Angular and will perform most of the work for you. For example, let's say that you have a Todo entity in the server. The standard CRUD API for this will be:

GET => baseUrl/todos

GET => baseUrl/todos/id

POST => baseUrl/todos

UPDATE => baseUrl/todos/id

REMOVE => baseUrl/todos/id

So, to simplify the work with these APIs, we'll introduce the HttpClientEntityService class. This is how it works:

@Injectable({ providedIn: 'root' })
@EntityServiceConfig({ ... })
export class TodosService extends HttpClientEntityService<TodosState> {
  constructor(protected store: TodosStore) {
    super(store);
  }
}

// app.module
@NgModule({
  ...
  providers: [
    { provide: HTTP_CLIENT_ENTITY_SERVICE_CONFIG, useValue: { 
        baseUrl: 'https://jsonplaceholder.typicode.com' 
    }}
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

The HttpClientEntityService will expose all the GET and CRUD methods you need, so you can easily use it from the component:

export class TodosPageComponent {
  posts$ = this.todosQuery.selectAll();
  loading$ = this.todosQuery.selectLoading();

  constructor(
    private todosQuery: TodosQuery,
    private todosService: TodosService
  ) {}

  ngOnInit() {
    this.todosService.get().subscribe();
  }

  fetchOne() {
    this.todosService.get(id).subscribe(console.log);
  }

  add() {
    this.todosService.add(entity).subscribe({
      error() { // show error }
    });
  }

  update() {
    this.todosService.add(id, entity).subscribe();
  }

  remove() {
    this.todosService.delete(1).subscribe();
  }
}

Each method also takes a config object where you could pass custom headers, params, etc. In addition to that, we'll create a dispatcher that you can inject and listens for any action you want:

export class MyComponent {

  constructor(
    private dispatcher: EntityServiceDispatcher
  ) {}

  ngOnInit() {
    this.dispatcher.action$
      .pipe(
        ofType('success'),
        filterMethod('DELETE')
      )
      .subscribe(action => {
          action = { method: "DELETE"
                     payload: {}
                     storeName: "todos"
                     type: "success"
                   }
      });
  }
 }

This feature is work in progress. We would love to hear your feedback and ideas.

In progress enhancement help wanted

All 6 comments

"Akita wise" this is indeed a good suggestion - most of the users are indeed implementing it on their own already I assume.
In our specific case, the server base URL is only available after user logs in due to the fact that it not always has a static host (cyber security system).
I think that adding the ability to to configure the base in run time could be helpful for others as well.
Also, our BE accepts most of the UD operations in POST for different reasons - so having defaults for HTTP methods would be nice to not specify them explicitly each time.
But that's only us :)

Good Idea, but this will be in relation with Akita store ?
Can this service will be linked easily and quickly to a store, so that the store's actions are saved directly in the api, at the same time as in the store. Because often in some cases, we do not need to do more. So with this service connected to the blind, this would limit the boilerplate code.

Sure, it will update the store.

Hi @NetanelBasal! There is no post() method in NgEntityService. For example if I want to verify an email, I need to inject the HttpClient and call the post() method separately:

@NgEntityServiceConfig()
@Injectable({providedIn: 'root'})
export class UserService extends NgEntityService<UserState> {
  constructor(protected store: UserStore, private _http: HttpClient) {
    super(store);
  }

  load() {
    cacheable(this.store, super.get()).subscribe();
  }

  checkEmail$(user: Partial<User>) {
    return this._http.post<string[]>(`${environment.api}/users/validate`, user);
  }
}

It would be great if I could perform the post() method using NgEntityService like this (same class, checkEmail$ method is changed):

checkEmail$(user: Partial<User>) {
  return super.post<string[]>('', user, {
    resourceName: 'users/validate', // with this attribute I could change the API endpoint
  });
}

Is this possible to implement?

Thank you in advance!

@vladimirdrayling, the goal of this service is entities management. I can expose the HTTP provider so you wouldn't need to inject it:

checkEmail$(user: Partial<User>) {
  return this.http.post<string[]>('', user);
}

@NetanelBasal thank you for speedy response! That would be great!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RonnieRocket147 picture RonnieRocket147  路  5Comments

adraax picture adraax  路  4Comments

NathanAlcantara picture NathanAlcantara  路  4Comments

twittwer picture twittwer  路  7Comments

hoisel picture hoisel  路  5Comments