# Discovery service

This improvement will be an opportunity to learn about a very interesting and useful tool. We could make that all **non-public routes** receive the <mark style="color:blue;">`@ApiUnauthorizedResponse()`</mark> decorator, which indicates that a route may return a response with the <mark style="color:blue;">UNAUTHORIZED</mark> status. The same could be thought about the **routes that require roles**, and the <mark style="color:blue;">`@ApiForbiddenResponse()`</mark> decorator. To achieve this, we can leverage the <mark style="color:blue;">`DiscoveryService`</mark>, which allows for accessing references to all **controllers** and **providers** during system startup, which is quite useful for appending decorators dynamically, for instance. Let's then begin.

First, let's create a module to encapsule these documentation mappers.

```sh
nest g mo docs
```

Then, let's create a class for the **Unauthorized mapper**.

```sh
nest g cl docs/docs-unauthorized-mapper
```

{% hint style="info" %}
Please perform the following:

* Alter the filename to end in <mark style="color:purple;">.mapper</mark>
* Add <mark style="color:blue;">`@Injectable()`</mark>, so that it can be accessed by the **Nest IoC container**
  {% endhint %}

Now, in the module, we should add this mapper to the <mark style="color:blue;">`providers`</mark>, and the <mark style="color:blue;">`DiscoveryModule`</mark> to the <mark style="color:blue;">`imports`</mark>, in order to use the <mark style="color:blue;">`DiscoveryService`</mark>.

The next step is to, back in the class, make it implement the <mark style="color:blue;">`OnApplicationBootstrap`</mark> interface. This forces the class to implement the namesake method, which executes when the app is **starting**. Now, the first step in this method will be to obtain a reference to the <mark style="color:blue;">`controllers`</mark> by using the <mark style="color:blue;">`DiscoveryService`</mark>, which we also should inject now.

```typescript
const controllers = this.discoveryService.getControllers();
```

We'll then iterate over each controller. Notice that they were returned inside **wrappers**.

```typescript
controllers.forEach((wrapper) => {
  // ...
});
```

Next, we'll obtain the <mark style="color:blue;">`instance`</mark> from inside each <mark style="color:blue;">`wrapper`</mark>, and then obtain the <mark style="color:blue;">`prototype`</mark> from the <mark style="color:blue;">`instance`</mark>. This will be necessary afterwards.

```typescript
const { instance } = wrapper;
const prototype = Object.getPrototypeOf(instance);
```

Some parts now will be a bit familiar. We should then use a <mark style="color:blue;">`reflector`</mark> to get the <mark style="color:blue;">isPublic</mark> metadata from the **controller** itself. Here we have no access to the <mark style="color:blue;">`context`</mark> like we did inside the guards, so we should pass <mark style="color:blue;">`instance.constructor`</mark>. If the controller is **public**, simply return.

```typescript
const isControllerPublic = this.reflector.get<boolean>(
  IS_PUBLIC_KEY,
  instance.constructor,
);
if (isControllerPublic) return;
```

After that, we'll get the names of the controller's route handlers by using the <mark style="color:blue;">`metadataScanner`</mark> (also to be injected). This can be achieved by using the <mark style="color:blue;">`prototype`</mark>. Sequentially, we may obtain references to the actual route handlers by indexing these names inside the controller <mark style="color:blue;">`instance`</mark>.

```typescript
const routeNames = this.metadataScanner.getAllMethodNames(prototype);
const routes = routeNames.map((name) => instance[name]);
```

Nearing the end, we'll now iterate over each <mark style="color:blue;">`route`</mark>.

```typescript
routes.forEach((route) => {
  // ...
});
```

The same process will now be performed with each handler: if it's **public**, simply return.

```typescript
const isPublic = this.reflector.get<boolean>(IS_PUBLIC_KEY, route);
if (isPublic) return;
```

Finally, to append this decorator to the route, use it without <mark style="color:blue;">@</mark>, and use a second pair of parentheses, passing the <mark style="color:blue;">`route`</mark>.

```typescript
ApiUnauthorizedResponse({ description: 'Unauthorized' })(route);
```

And we're done! Now these routes will be automatically decorated for us.

<mark style="color:green;">**Commit**</mark> - Using discovery service to automatically document routes

Let's now create the **Forbidden mapper**. Please repeat the aforementioned procedure, and I'll detail then what will be different.

In this case, we'll check if the controller is **protected**, that is, if it requires roles.

```typescript
const isControllerProtected = !!this.reflector.get<Role[]>(
  ROLES_KEY,
  instance.constructor,
);
```

If it is, all its routes will be marked with the decorator and this iteration ends.

```typescript
if (isControllerProtected) {
  routes.forEach((route) => {
    ApiForbiddenResponse({ description: 'Forbidden' })(route);
  });
  return;
}
```

If not, we'll then check each handler, and it won't be marked if it's <mark style="color:red;">not</mark> protected.

```typescript
const isProtected = !!this.reflector.get<Role[]>(ROLES_KEY, route);
if (!isProtected) return;
```

We have now covered both cases with dynamic documentation. Outstanding!

<mark style="color:green;">**Commit**</mark> - Documenting forbidden routes automatically


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kinesis-school-of-programming.gitbook.io/nestjs-unleashed/extra-module-3-openapi-specification/discovery-service.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
