# Controllers
Controllers can be generated by running `hatch controller <ModuleName>`

Controllers define HTTP routes that the server handles. Controller classes are registered by the 
[composeServer.ts](../../README.md#composeserverts) module, and must be annotated with the `@controller` annotation 
exported by `hatch-server`. 

## Routes
Within a Controller, routes are defined using decorators within the `route` namespace exported by `hatch-server`. These 
decorators use the same routing syntax that is used by [express](https://expressjs.com), including support for route 
parameters. A simple example of a GET route with a person ID parameter can be seen below:

```typescript
import {
  BasicRouteParams,
  controller,
  route,
  ServerMiddleware,
} from '@launchtray/hatch-server';
import {inject, Logger} from '@launchtray/hatch-util';

@controller()
export default class ExampleController implements ServerMiddleware {
  // Note: dependencies can be injected into the constructor
  constructor(@inject('Logger') private readonly logger: Logger) {
  }

  @route.get('/api/person/:id')
  public personEndpoint(params: BasicRouteParams) {
    params.res.status(200).send('Person: ' + params.req.params.id);
  }

  ...
```

As shown above, route methods can take in a parameter of type `BasicRouteParams`, which contains the Express 
[Request](https://expressjs.com/en/api.html#req) and [Response](https://expressjs.com/en/api.html#res) objects, the 
`next` callback passed to the request handler, a Logger instance, any cookies, and the auth header.

However, when a request comes in, the server creates a new dependency injection container specifically for that request.
This container is a child of the root container that is used by [composeServer.ts](../../README.md#composeserverts) and
[composeCommon.ts](../../README.md#composecommonts) to register dependency classes. The only objects that get registered
into the child container that are not also in the root container are the fields of `BasicRouteParams`. The parameters of
route handler methods are actually auto-resolved using this container. This means that not only could we define the 
method with a single `BasicRouteParams` parameter, but we could also use any parameters, as long as they can be resolved 
by the child container described above. For example, imagine a very simple `HTTPResponder` class which depends on 
`BasicRouteParams`:

```typescript
import {BasicRouteParams} from '@launchtray/hatch-server';
import {containerSingleton} from '@launchtray/hatch-util';

@containerSingleton()
export default class HTTPResponder {
  constructor(public readonly params: BasicRouteParams) {}

  public ok(body?: any) {
    this.params.res.status(200).send(body);
  }
}
```
Since this class is annotated as injectable (via `@containerSingleton()`), and since it only depends on things that are
registered in an HTTP request's child container (in this case, just `BasicRouteParams`), we can use it as a parameter to
a route like so:

```typescript
@route.get('/api/example')
public exampleEndpoint(responder: HTTPResponder) {
  this.logger.info('Example endpoint called');
  responder.ok('Example GET');
}
```
#### Route metadata
Metadata about a route can be provided via a second parameter to the route. 

##### OpenAPI definitions
Route metadata is primarily used for providing OpenAPI documentation and a Swagger interface. For example, parameters
for a route can be documented:
```typescript
  @route.get('/api/example/person/:id', {
    parameters: {
      id: {
        description: 'The person\'s identifier',
      },
    },
  })
  public personEndpoint(params: BasicRouteParams) {
    params.res.status(200).send('Person: ' + params.req.params.id);
  }
```

See `APIMetadataParameters` in `hatch-server` for what OpenAPI metadata can be defined for a route.

##### Route tokens
Routes can be marked with "tokens" so that metadata about them can be retrieved at runtime via the dependency injection 
system. 

For example, marking a route with the `AUTH_WHITELIST_KEY` token from `hatch-server-user-management` will result
in the route being whitelisted for user authentication:

```typescript
import {AUTH_WHITELIST_KEY} from '@launchtray/hatch-server-user-management';

...

  @route.get('/api/authWhitelistExample', {tokens: [AUTH_WHITELIST_KEY]})
  public exampleEndpoint(responder: HTTPResponder) {
    this.logger.info('Example whitelisted endpoint called');
    responder.ok('Example GET');
  }

...
```

This works because `UserManagementController` has a catch-all endpoint which resolves all routes registered with the 
`AUTH_WHITELIST_KEY` token and adds those routes to the whitelist. More specifically, `UserManagementController`
resolves this whitelist by taking in `UserInfoRequest`, which has all `AUTH_WHITELIST_KEY` values injected as 
constructor parameters.

Note that route tokens are not registered until after the controllers are created. As such, resolving routes by their 
tokens should typically be done at a point well-past application initialization, e.g. via objects injected during
requests similar to the `UserInfoRequest` example above. 

### WebSockets
In addition to HTTP method routes, WebSocket server routes can be defined. These routes make use of the 
[ws library](https://github.com/websockets/ws). An example WebSocket route that broadcasts messages to all other clients
is shown below. Note that route parameters (in this case an ID) can also be used with WebSocket routes.

```typescript
@route.websocket('/ws/:id')
public async handleWebsocket(params: WebSocketRouteParams) {
  const ws = params.webSocket; 

  // ws is simply a web socket object from the ws library

  ws.on('message', (msg: string) => {
    params.webSocketServer.clients.forEach((client: WebSocket) => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(params.req.params.id + ': ' + msg);
      }
    });
  });
}
```

## Health checks
Each controller can define health checks that contribute to the overall health result returned by `/api/health`,
`/api/health/liveness` and `/api/health/readiness`. This can be done via `@livenessCheck()` and `@readinessCheck()`
decorators. Each controller can have multiple checks of each type.

### Liveness checks
Liveness checks can be used for signaling that the service is alive or if it needs to be restarted.

To signal that functionality is healthy, methods decorated with `@livenessCheck()` can return `true` or 
`LivenessState.CORRECT`. Liveness checks that throw an error, return `false` or return `LivenessState.BROKEN` will cause 
the overall health check to indicate that the service is down.

### Readiness checks
Readiness checks can be used for signaling that the service is not only alive, but is ready to receive traffic.

To signal that functionality is ready, methods decorated with `@readinessCheck()` can return `true` or 
`ReadinessCheck.ACCEPTING_TRAFFIC`. Readiness checks that throw an error, return `false` or return 
`ReadinessCheck.REFUSING_TRAFFIC` will cause the overall health check to indicate that the service is out of service.

### Application info
By default, `/api/health/info` will respond with basic information about the build, like commit ID, package name and
version, build time, etc. Custom data can be returned by this endpoint by adding controller methods decorated with
`@appInfoProvider()`. These methods can be asynchronous and should return an object representing key-value pairs to be
included in the response for `/api/health/info`.
