![Node Decorators](https://github.com/serhiisol/node-decorators/blob/master/decorators.png?raw=true)

# Http
## Installation
Main dependencies
```
npm install @decorators/server @decorators/di --save
```
Adapter specific imports
```
npm install express body-parser --save
```
Or
```
npm install fastify @fastify/cookie @fastify/static @fastify/view --save
```
Or
```
npm install koa koa-bodyparser koa-mount koa-static koa-views --save
```

## Example
Fully working example can be found in [example](example) folder.

## Application
In order to create an application, use `Application` class with app root module:
```typescript
const app = await Application.create(AppModule, server?);
```

Application instance provides an `inject` method to retrieve instances of any provided objects:
```typescript
const app = await Application.create(AppModule);
const module = await app.inject<HttpModule>(HttpModule);

module.use(json());

await module.listen(3000);
```

## Modules
* `HttpModule` - main module to start an application:
```typescript
import { Module } from '@decorators/server';
import { HttpModule } from '@decorators/server/http';
import { ExpressAdapter } from '@decorators/server/express';

@Module({
  modules: [
    HttpModule.create(ExpressAdapter),
  ],
})
export class AppModule { }
```

## Adapters
* `ExpressAdapter` - adapter for [express](https://github.com/expressjs/express) from `@decorators/server/express`
* `FastifyAdapter` - adapter for [fastify](https://github.com/fastify/fastify) from `@decorators/server/fastify`
* `KoaAdapter` - adapter for [koa](https://github.com/koajs/koa) from `@decorators/server/koa`

Adapter can be instantiated with existing application (for example express application):
```ts
HttpModule.create(new ExpressAdapter(app));
```

## Payload vaidation
Package supports [class-validator](https://github.com/typestack/class-validator) and [class-transformer](https://github.com/typestack/class-transformer) packages, basic types validation is supported as well:
```typescript
@Get(':id', 200)
post(@Params() user: UserDto) {
  return user;
}
```

## Pipes
Pipes allow to add additional "interceptors" before and after main route function.
In order to implement a pipe import `ProcessPipe` interface and implement it:

```typescript
import { PipeHandle, ProcessPipe } from '@decorators/server';
import { HttpContext } from '@decorators/server/http';

export class TransformPipe implements ProcessPipe {
  async run(context: HttpContext, handle: PipeHandle<string>) {
    const message = await handle();

    return message.toLocaleString();
  }
}
```

Add `@Pipe` decorator to the method or to entire controller:
```typescript
@Pipe(TransformPipe)
process(@Body() body: object)
```

Pipes can be used both for controller and methods.

## Injectables
Global server pipes can be applied by providing them via `GLOBAL_PIPE` injectable with `multi` (see [di](../di) package for details) flag:
```typescript
import { GLOBAL_PIPE, Module } from '@decorators/server';

@Module({
  providers: [
    {
      provide: GLOBAL_PIPE,
      useClass: ServerPipe,
      multi: true,
    },
  ],
})
export class AppModule { }
```

### App prefix
To create global application prefix (aka version, namespace) use `APP_VERSION` injectable:
```typescript
import { APP_VERSION, Module } from '@decorators/server';

@Module({
  providers: [
    {
      provide: APP_VERSION,
      useValue: 'v1',
    },
  ],
})
export class AppModule { }
```

## Dependency injection
This module supports dependency injection provided by `@decorators/di` package. For convinience, `@decorators/server` reexports all decorators from `@decorators/di` package.

## Decorators
### Class
* `@Module(options: ModuleOptions)` - Defines a module (namespace) for DI providers, controllers etc.
* `@Controller(url: string, options?: Record<string, unknown>)` - Registers controller for base url with optional options
  * `options?.ignoreVersion` - ignore global version prefix (provided `APP_VERSION`), can be useful to setup global handlers, such as 404 handling
* `@Pipe(pipe: ClassConstructor<ProcessPipe>)` - Registers a pipe for a controller

### Method
* `@Pipe(pipe: ClassConstructor<ProcessPipe>)` - Registers a pipe for a method

#### @decorators/server/http
* `@Delete(url: string, status?: number)` - Registers delete route
* `@Get(url: string, status?: number)` - Registers get route
* `@Head(url: string, status?: number)` - Registers head route
* `@Options(url: string, status?: number)` - Registers options route
* `@Patch(url: string, status?: number)` - Registers patch route
* `@Post(url: string, status?: number)` - Registers post route
* `@Put(url: string, status?: number)` - Registers put route

* `@Render(template: string)` - Renders a template in the configured views folder
```typescript
const app = await Application.create(AppModule);
const module = await app.inject<HttpModule>(HttpModule);

module.set('views', join(__dirname, '/views'));
```

### Parameter
* `@Body(paramName?: string, paramValidator?: Validator)` - Request body object or single body param
* `@Cookies(paramName?: string, paramValidator?: Validator)` - Request cookies or single cookies param
* `@Headers(paramName?: string, paramValidator?: Validator)` - Request headers object or single headers param
* `@Params(paramName?: string, paramValidator?: Validator)` -  Request params object or single param
* `@Query(paramName?: string, paramValidator?: Validator)` - Request query object or single query param
* `@Request(paramName?: string)` - Returns request object or any other object available in req object itself
* `@Response(paramName?: string)` - Returns response object or any other object available in response object itself

## Custom Decorators
Package exports two main helpers to create custom decorators:
* `Decorate` - allows to create custom class or method decorators
```typescript
import { Decorate } from '@decorators/server';
// ...
@Decorate('hasAccess', 'granted')
create() {}
```

To read custom metadata use `Reflector` injectable and its `getMeatada` method:
```typescript
@Injectable()
export class AccessPipe implements ProcessPipe {
  constructor(private reflector: Reflector) { }

  async run(context: HttpContext, handle: PipeHandle<string>) {
    const access = this.reflector.getMetadata('hasAccess', context.getHandler());
    const req = context.getRequest<Request>();

    if (access === req.query.access) {
      return handle();
    }

    throw new ApiError('unauthorized');
  }
}
```

* `createParamDecorator(factory: (context: Context) => any)` - allows to create custom parameter decorators
```typescript
import { createParamDecorator } from '@decorators/server';

function AccessParam() {
  return createParamDecorator((context: HttpContext) => {
    const req = context.getRequest<Request>();

    return req.query.access;
  });
}

// ...
create(@AccessParam() access: string) {}
```

---

# Swagger
Swagger decorators are available in
```typescript
import { SwaggerModule } from '@decorators/server/swagger';
```

To start with swagger decorators provide `SwaggerModule` in the `AppModule`, for example:

```typescript
import { SwaggerModule } from '@decorators/server/swagger';

@Module({
  modules: [
    HttpModule.create(ExpressAdapter),
    SwaggerModule.forRoot({
      description: 'Decorators Example App',
      title: '@decorators/server',
    }),
    ...
  ],
})
export class AppModule { }
```

## Decorators
### Method
* `@ApiOperation(operation: OpenAPIV3_1.OperationObject)` - Registers an operation
* `@ApiResponse(description: string, type?: ClassConstructor)` - Registers simple response for a method. This decorator uses status provided by the route decorator, e.g. `@Get(route, status)`.
* `@ApiResponseSchema(responses: ApiResponses)` - Registers a response for a method. This method accepts more complex types of responses, if method returns more than one.
* `@ApiBearerAuth()` - Defines a bearer authentication method for a route
* `@ApiSecurity(security: OpenAPIV3_1.SecuritySchemeObject)` - Defines more complex authentication methods for a route.

### Property
* `@ApiParameter(parammeter: { description?: string })` - Specifies a description for a property defined in the class-decorator based classes

---

# Sockets
## Installation
```
npm install socket.io --save
```

## Setup
To start provide `SocketsModule` with one of the provided adapters
```typescript
import { Module } from '@decorators/server';
import { SocketsModule } from '@decorators/server/sockets';
import { SocketIoAdapter } from '@decorators/server/socket-io';

@Module({
  modules: [
    SocketsModule.create(SocketIoAdapter),
  ],
})
export class AppModule { }
```

Inject `SocketsModule` to start listeners
```ts
const app = await Application.create(AppModule);
const module = await app.inject<SocketsModule>(SocketsModule);

await module.listen();
```

Add a controller
```ts
import { Controller } from '@server';
import { Connection, Disconnect, Event, Param } from '@server/sockets';

@Controller()
export class EventsController {
  @Connection()
  connection() { }

  @Disconnect()
  disconnect() { }

  @Event('message')
  event(@Param() message: MessageType) {
    return message;
  }
}
```

## Errors
If error occurres, system will send `error` event to the client with an error object.

## Adapters
* `SocketIoAdapter` - adapter for [socket.io](https://socket.io) from `@decorators/server/socket-io`

## Payload vaidation
Validation works similarly to http module see [validation](#payload-vaidation) section.

## Pipes
Pipes work similarly to http module see [pipes](#pipes) section.

## Decorators
### Method
* `@Connection()` - Registers `connection` event.
* `@Disconnect()` - Registers `disconnect` event
* `@Disconnecting()` - Registers `disconnecting` event
* `@Event(event: string)` - Register custom event. Returned data from the handler will be sent through Ack.

### Parameter
* `@Param(paramValidator?: Validator)` - Returns param sent via `emit`. Not available for `connection`, `disconnect` and `disconnecting` events. Multiple params can be used to receive all the params:
```ts
message(
  @Param() message1: string,
  @Param() message2: string,
) { }
```

* `@Server()` - Returns server object
* `@Socket()` - Returns socket object
