1.0.18 • Published 1 year ago

@altcrm/altexpress v1.0.18

Weekly downloads
-
License
Apache License 2....
Repository
-
Last release
1 year ago

ALTEXPRESS Backend Engine

! Attention ! The engine is in development. Functionality can be changed.

Get started

  1. Create a controller:
import { Request, Response, Controller, Get, Post, Put, Delete } from "@altcrm/altexpress";

@Controller("/clients")
export class ClientController {

    @Get("/:id")
    public async get(req: Request, res: Response): Promise<any> {
        const client = await service.findById(req.params.id);
        return { client };
    }
}
  1. Create a module:
import { Module } from "@altcrm/altexpress";

@Module({
    controllers: [ClientController]
})
export class ApplicationModule {}
  1. Create an application:
import { Application } from "@altcrm/altexpress";
import { ApplicationModule } from "./application.module";

async function startServer(port: number): Promise<void> {
    try {
        // create application and specify main module
        const app = new Application(ApplicationModule);

        // - !!! DO NOT USE !!! "express.urlencoded", "express.json" -
        // - already defined inside the "Application":
        // app.use(express.urlencoded({ extended: true }));
        // app.use(express.json());
        
        // - to specify global API prefix:
        // app.setGlobalPrefix("/api/v1");

        // - to specify middlewares:
        // app.use(morgan("dev"));
        // app.use(cors());

        console.log("Starting API...");
        await app.listen(port);
        console.log(`Server is running on port ${port}`);
    } catch (e) {
        console.error(e);
    }
}

// start the server
startServer(3000);

Tricks

Returning Method Result

  1. Simple. Just return a body (http status is 200 by default):
import { Request, Response, Controller, Post } from "@altcrm/altexpress";

@Controller("/clients")
export class ClientController {

    @Post("/")
    public async create(req: Request, res: Response): Promise<any> {
        const client = await service.create(req.body.dto);
        return { client };
    }
}
  1. Custom status and body. Return HttpResponse object:
import { Request, Response, Controller, Post, HttpResponse } from "@altcrm/altexpress";

@Controller("/clients")
export class ClientController {

    @Post("/")
    public async create(req: Request, res: Response): Promise<any> {
        const client = await service.create(req.body.dto);
        return new HttpResponse(201, { client });

        // or
        // return HttpResponse.successCreated({ client });
    }
}
  1. Error handling:
import { Request, Response, Controller, Post, HttpResponse, HttpException } from "@altcrm/altexpress";

@Controller("/clients")
export class ClientController {

    @Post("/")
    public async create(req: Request, res: Response): Promise<any> {
        try {
            // ...
        } catch (e: any) {
            return HttpResponse.error(e);

            // or
            // const err = new HttpException(400, e.message);
            // return HttpResponse.error(err);
        }
    }
}
  1. Error handling by engine. Application will catch and handle exceptions:
import { Request, Response, Controller, Post, HttpResponse, HttpException } from "@altcrm/altexpress";

@Controller("/clients")
export class ClientController {

    @Post("/")
    public async create(req: Request, res: Response): Promise<any> {
        // ...
        throw new HttpException(403, "Forbidden");

        // or
        // throw new Error("Custom error.");
    }
}

API Versioning

  1. Create controllers and modules for your business logic:
@Controller("/clients")
export class ClientControllerV1 {

    @Get("/:id")
    public async get(req: Request, res: Response): Promise<any> {
        // ...
    }
}

@Controller("/clients")
export class ClientControllerV2 {

    @Get("/:id")
    public async get(req: Request, res: Response): Promise<any> {
        // ...
    }
}

@Module({
    controllers: [ClientControllerV1]
})
export class ClientModuleV1 {}

@Module({
    controllers: [ClientControllerV2]
})
export class ClientModuleV2 {}
  1. Create modules for different API versions with prefix specified:
@Module({
    prefix: "/api/v1",
    modules: [ClientModuleV1]
})
export class ApiModuleV1 {}

@Module({
    prefix: "/api/v2",
    modules: [ClientModuleV2]
})
export class ApiModuleV2 {}
  1. Add modules to main application module:
@Module({
    modules: [ApiModuleV1, ApiModuleV3]
})
export class ApplicationModule {}

Now the both routes are available:

/api/v1/clients/11111
/api/v2/clients/22222

Middlewares

  1. Create a middleware (for example, guard for authentication):
import passport from "passport";

export default class Guard {
    public static Auth = passport.authenticate("jwt", { session: false });
}
  1. Add middleware to method decorator:
@Controller("/clients")
export class ClientController {

    @Get("/:id", Guard.Auth)
    public async get(req: Request, res: Response): Promise<any> {
        // ...
    }
}

Argument decorators

You can also use argument decorators to make your code cleaner:

@Controller("/clients")
export class ClientController {

    @Get("/search", Guard.Auth)
    public async search(
        @Query("search") search: string
    ): Promise<Client[]> {
        return await this.clientService.search(search);
    }

    @Get("/:id", Guard.Auth)
    public async get(
        @Params("id") id: string
    ): Promise<Client> {
        return await this.clientService.get(id);
    }

    @Put("/:id", Guard.Auth)
    public async update(
        @Params("id") id: string,
        @Body() dto: ClientUpdateDto
    ): Promise<Client> {
        return await this.clientService.update(id, dto);
    }

    @Patch("/:id", Guard.Auth)
    public async updatePhone(
        @Params("id") id: string,
        @Body("phone") phone: string
    ): Promise<Client> {
        return await this.clientService.updatePhone(id, phone);
    }
}

Use request and response objects

The controller is instantiated at each request. The instance has properties request, response, which can be accessed from every method. You can extends ControllerBase class to access this properties from every method.

@Controller("/clients")
export class ClientController extends ControllerBase {
    @Get("/:id", Guard.Auth)
    public async get(): Promise<Client> {
        const id = this.request.params.id // here
        return await this.clientService.get(id);
    }
}

Use dependency injection

You can use the dependency injection for controllers:

@Controller("/orders")
export class OrderController extends ControllerBase {
    public constructor(
        @Inject(OrderService) private readonly orderService: OrderService,
        @Inject(ClientService) private readonly clientService: ClientService,
        @Inject(DateTimeService) private readonly dateTimeService: DateTimeService,
    ) {
        super();
    }
}

or services:

export class OrderService {
    public constructor(
        @Inject(DateTimeService) private readonly dateTimeService: DateTimeService,
    ) { }
}

Dependencied should be registered in module's services section:

@Module({
    controllers: [
        OrderController,
        ClientController,
    ],
    services: [
        // Provide only the type for injection:
        OrderService,

        // It is an equivalent to:
        { type: ClientService, lifetime: ServiceLifetime.Scoped },

        // You can provide concrete implementation for injection:
        { type: DateTimeService, concrete: DateTimeServiceFake, lifetime: ServiceLifetime.Singleton },
    ],
})
export class OrderModule {}

Service lifetime could be one of the next values:

  • Scoped (default) - Service is instantiated once for every request.
  • Transient - Service is instantiated once for every call.
  • Singleton - Service is instantiated once for the entire module lifetime.
1.0.18

1 year ago

0.1.17

2 years ago

0.0.16

3 years ago

0.0.15

3 years ago

0.0.13

3 years ago

0.0.10

3 years ago

0.0.11

3 years ago

0.0.12

3 years ago

0.0.9

3 years ago

0.0.8

3 years ago

0.0.7

3 years ago

0.0.6

3 years ago

0.0.5

3 years ago

0.0.4

3 years ago

0.0.3

3 years ago

0.0.2

3 years ago

0.0.1

3 years ago