diff --git a/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/POST.md b/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/POST.md new file mode 100644 index 0000000000..61ca43b250 --- /dev/null +++ b/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/POST.md @@ -0,0 +1,308 @@ +# Introducing the Angular Service Proxy Generation + +Angular Service Proxy System **generates TypeScript services and models** to consume your backend HTTP APIs developed using the ABP Framework. So, you **don't manually create** models for your server side DTOs and perform raw HTTP calls to the server. + +ABP Framework has introduced the **new** Angular Service Proxy Generation system with the version 3.1. While this feature was available since the [v2.3](https://blog.abp.io/abp/ABP-Framework-v2_3_0-Has-Been-Released), it was not well covering some scenarios, like inheritance and generic types and had some known problems. **With the v3.1, we've re-written** it using the [Angular Schematics](https://angular.io/guide/schematics) system. Now, it is much more stable and feature rich. + +This post introduces the service proxy generation system and highlights some important features. + +## Installation + +### ABP CLI + +You need to have the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) to use the system. So, install it if you haven't installed before: + +````bash +dotnet tool install -g Volo.Abp.Cli +```` + +If you already have installed it before, you can update to the latest version: + +````shell +dotnet tool update -g Volo.Abp.Cli +```` + +### Project Configuration + +> If you've created your project with version 3.1 or later, you can skip this part since it will be already installed in your solution. + +For the solution was created before v3.1, follow the steps below to configure your angular project: + +* Add `@abp/ng.schematics` package to the `devDependencies` of the Angular project (run the following command in the root folder of the angular application): + +````bash +npm install @abp/ng.schematics --save-dev +```` + +- Add `rootNamespace` entry into the `/apis/default/` section in the `/src/environments/environment.ts`, as shown below: + +```json +apis: { + default: { + ... + rootNamespace: 'Acme.BookStore' //<-- ADD THIS + }, +} +``` + +`Acme.BookStore` should be replaced by the root namespace of your .NET project. This ensures to not create unnecessary nested folders while creating the service proxy code. + +## Basic Usage + +### Project Creation + +Assuming you've created your project with the Angular UI. + +Example (using the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI)): + +````bash +abp new AngularProxyDemo -u angular +```` + +#### Run the Application + +The backend application must be up and running to be able to use the service proxy code generation system. + +> See the [getting started](https://docs.abp.io/en/abp/latest/Getting-Started?UI=NG&DB=EF&Tiered=No) guide if you don't know how create and to run the project. + +### Backend + +Assume that we have an `IBookAppService` interface: + +````csharp +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace AngularProxyDemo.Books +{ + public interface IBookAppService : IApplicationService + { + public Task> GetListAsync(); + } +} +```` + +That uses a `BookDto` defined as shown: + +```csharp +using System; +using Volo.Abp.Application.Dtos; + +namespace AngularProxyDemo.Books +{ + public class BookDto : EntityDto + { + public string Name { get; set; } + + public DateTime PublishDate { get; set; } + } +} +``` + +And implemented as the following: + +```csharp +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Application.Services; + +namespace AngularProxyDemo.Books +{ + public class BookAppService : ApplicationService, IBookAppService + { + public async Task> GetListAsync() + { + //TODO: get books from a database... + } + } +} +``` + +It simply returns a list of books. You probably want to get the books from a database, but it doesn't matter for this article. + +### HTTP API + +Thanks to the conventional API controllers system of the ABP Framework, we don't have too develop API controllers manually. Just **run the backend (*HttpApi.Host*) application** that shows the [Swagger UI](https://swagger.io/tools/swagger-ui/) by default. You will see the GET API for the books: + +![swagger-book-list](swagger-book-list.png) + +### Service Proxy Generation + +Open a command line in the root folder of the Angular application and execute the following command: + +````bash +abp generate-proxy +```` + +It should produce an output like the following: + +````bash +CREATE src/app/shared/models/books/index.ts (142 bytes) +CREATE src/app/shared/services/books/book.service.ts (437 bytes) +... +```` + +> `generate-proxy` command can take some some optional parameters for advanced scenarios (like [modular development](https://docs.abp.io/en/abp/latest/Module-Development-Basics)). You can take a look at the [documentation](https://docs.abp.io/en/abp/latest/UI/Angular/Service-Proxies). + +It basically creates two files; + +src/app/shared/services/books/**book.service.ts**: This is the service that can be injected and used to get the list of books; + +````typescript +import { RestService } from '@abp/ng.core'; +import { Injectable } from '@angular/core'; +import type { BookDto } from '../../models/book'; + +@Injectable({ + providedIn: 'root', +}) +export class BookService { + apiName = 'Default'; + + getList = () => + this.restService.request({ + method: 'GET', + url: `/api/app/book`, + }, + { apiName: this.apiName }); + + constructor(private restService: RestService) {} +} + +```` + +src/app/shared/models/books/**index.ts**: This file contains the modal classes corresponding to the DTOs defined in the server side; + +````typescript +import type { EntityDto } from '@abp/ng.core'; + +export interface BookDto extends EntityDto { + name: string; + publishDate: string; +} +```` + +You can now inject the `BookService` into any Angular component and use the `getList()` method to get the list of books. + +### About the Generated Code + +The generated code is; + +* **Simple**: It is almost identical to the code if you've written it yourself. +* **Splitted**: Instead of a single, large file; + * It creates a separate `.ts` file for every backend **service**. **Model** (DTO) classes are also grouped per service. + * It understands the [modularity](https://docs.abp.io/en/abp/latest/Module-Development-Basics), so creates the services for your own **module** (or the module you've specified). +* **Object oriented**; + * Supports **inheritance** of server side DTOs and generates the code respecting to the inheritance structure. + * Supports **generic types**. + * Supports **re-using type definitions** across services and doesn't generate the same DTO multiple times. +* **Well-aligned to the backend**; + * Service **method signatures** match exactly with the services on the backend services. This is achieved by a special endpoint exposed by the ABP Framework that well defines the backend contracts. + * **Namespaces** are exactly matches to the backend services and DTOs. +* **Well-aligned with the ABP Framework**; + * Recognizes the **standard ABP Framework DTO types** (like `EntityDto`, `ListResultDto`... etc) and doesn't repeat these classes in the application code, but uses from the `@abp/ng.core` package. + * Uses the `RestService` defined by the `@abp/ng.core` package which simplifies the generated code, keeps it short and re-uses all the logics implemented by the `RestService` (including error handling, authorization token injection, using multiple server endpoints... etc). + +These are the main motivations behind the decision of creating a service proxy generation system, instead of using a pre-built tool like [NSWAG](https://github.com/RicoSuter/NSwag). + +## Other Examples + +Let me show you a few more examples. + +### Updating an Entity + +Assume that you added a new method to the server side application service, to update a book: + +```csharp +public Task UpdateAsync(Guid id, BookUpdateDto input); +``` + +`BookUpdateDto` is a simple class defined shown below: + +```csharp +using System; + +namespace AngularProxyDemo.Books +{ + public class BookUpdateDto + { + public string Name { get; set; } + + public DateTime PublishDate { get; set; } + } +} +``` + +Let's re-run the `generate-proxy` command to see the result: + +```` +abp generate-proxy +```` + +The output of this command will be like the following: + +````bash +UPDATE src/app/shared/services/books/book.service.ts (660 bytes) +UPDATE src/app/shared/models/books/index.ts (217 bytes) +```` + +It tells us two files have been updated. Let's see the changes; + +**book.service.ts** + +````typescript +import { RestService } from '@abp/ng.core'; +import { Injectable } from '@angular/core'; +import type { BookDto, BookUpdateDto } from '../../models/books'; + +@Injectable({ + providedIn: 'root', +}) +export class BookService { + apiName = 'Default'; + + getList = () => + this.restService.request({ + method: 'GET', + url: `/api/app/book`, + }, + { apiName: this.apiName }); + + update = (id: string, input: BookUpdateDto) => + this.restService.request({ + method: 'PUT', + url: `/api/app/book/${id}`, + body: input, + }, + { apiName: this.apiName }); + + constructor(private restService: RestService) {} +} +```` + +`update` function has been added to the `BookService` that gets an `id` and a `BookUpdateDto` as the parameters. + +**index.ts** + +````typescript +import type { EntityDto } from '@abp/ng.core'; + +export interface BookDto extends EntityDto { + name: string; + publishDate: string; +} + +export interface BookUpdateDto { + name: string; + publishDate: string; +} +```` + +Added a new DTO class: `BookUpdateDto`. + +### Advanced Example + +In this example, I want to show a DTO structure using inheritance, generics, arrays and dictionaries. + diff --git a/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/abp-generate-proxy-output.png b/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/abp-generate-proxy-output.png new file mode 100644 index 0000000000..0a77d8e40f Binary files /dev/null and b/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/abp-generate-proxy-output.png differ diff --git a/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/swagger-book-list.png b/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/swagger-book-list.png new file mode 100644 index 0000000000..a0a062f636 Binary files /dev/null and b/docs/en/Blog-Posts/2020-09-07 Angular-Service-Proxies/swagger-book-list.png differ