# errors

### Error handling

When creating errors in Odin we use an `ExceptionBase` class which extends the default `Error` from JS but with more information used to debug errors.

#### `ExceptionBase` class content

**Available props**

| properties      | type                           | description                                                                                |
| --------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
| `cause`         | `Error` or `undefined`         | The cause of the error                                                                     |
| `code`          | `string`                       | The error code of the error                                                                |
| `correlationId` | `string`                       | The id of the request that failed retrieve using `RequestContextService.getRequestId()`    |
| `message`       | `string`                       | The message of the error                                                                   |
| `metadata`      | `ObjectLiteral` or `undefined` | The metadata related to the error usually we try to push the `className` and the `payload` |
| `name`          | `string`                       | The name of the `Error`                                                                    |
| `stack`         | `string` or `undefined`        | The stack trace of the error                                                               |

**Available functions**

| name       | parameters | descriptions                  |
| ---------- | ---------- | ----------------------------- |
| `toJSON()` | `void`     | Return a serialized exception |

#### Create / extends an ExceptionBase class

In order to create / extend an `ExceptionBase` class you have several parameters you could pass:

<br>

| parameters | type                           | description                                                                   |
| ---------- | ------------------------------ | ----------------------------------------------------------------------------- |
| `message`  | `string`                       | The message related to the error that occurred to give a bit more information |
| `cause`    | `Error` or `undefined`         | An `Error` class, the cause of the error                                      |
| `metadata` | `ObjectLiteral` or `undefined` | Additional metadata                                                           |

### Errors in Aggregates

The best way to handle error is to create a more specific error in Aggregates inside a file named `xxx.errors.ts` inside the `domain` folder of the Aggregate (eg: `src/modules/agency/domain/agency.errors.ts`)

For example:

```ts
export class AgencyNotFoundError extends ExceptionBase {
	static readonly message = 'Agency not found';

	public readonly code = 'AGENCY.NOT_FOUND';

	constructor(message?: string, cause?: Error, metadata?: ObjectLiteral) {
		super(message || AgencyNotFoundError.message, cause, metadata);
	}
}
```

And then use it:

```ts
const agency = await this.agencyRepo.findOne(agencyId);
if (res.isNone()) {
	throw new AgencyNotFoundError();
}
```

### Errors in `Services` and `Event handlers`

We usually setup a `trycatch` on either of those and then we manage the error inside the `catch`.

#### Services

Here is an example of how we handle the error inside of a services

```ts
try {
  ...
} catch (error: unknown) {
  if (error instanceof ExceptionBase) {
    if (!error.metadata) {
      error.metadata = {};
    }
    if (!error.metadata.payload) {
      error.metadata.payload = JSON.stringify(command);
    }
    if (!error.metadata.className) {
      error.metadata.className = this.constructor.name;
    }

    return Err(error);
  }

	if (error instanceof Error) {
		return Err(
			new AgencyError(error, {
				className: this.constructor.name,
				payload: JSON.stringify(command),
			}),
		);
	}

	return Err(
		new AgencyError(undefined, {
			className: this.constructor.name,
			payload: JSON.stringify(command),
		}),
	);
}
```

Since we don't know the instance of `error`, we handle every instance possible in order to always return an `ExceptionBase`.

In the first `if`, we check if the error is an instance of `ExceptionBase` and if so we check if the `className` and the `payload` are present in the `metadata`.

In the second `if`, we throw a generic Aggregate error if no specific exception has been specified.

Otherwise, we throw a generic Aggregate error.

#### Event handlers

Here is an example of how we handle the error inside of an event handler

```ts
try{
  ...
} catch (error) {
  if (!(error instanceof ExceptionBase)) {
    return this.publishError(
      new AgencyError(error, {
        className: this.constructor.name,
        payload: JSON.stringify(command),
      }),
    );
  }
  return this.publishError(error);
}
```

It's a bit different from the service cause here we have to publish the error in order to catch it due to some limitation from event-handler.

The `this.publishError` just emit an event with an `ErrorEvent`

```ts
public async publishError(error: ExceptionBase): Promise<void> {
		this.eventEmitter.emit(
			`error.${error.constructor.name}`,
			new ErrorEvent({
				code: error.code,
				message: error.message,
				metadata: { ...error.metadata },
				stack: error.stack,
				cause: error.cause,
			}),
		);
	}
```

### Catch Error globally, the error Aggregate

In order to easily handle errors in NestJS we have an error Aggregate which listen to all events with a wildcard `'error.*'`.

The error Aggregate has for objective to log the error and publish it to [Sentry](https://we-invest.sentry.io/projects/).

Most NestJS error are catch in a global interceptor (`src/common/application/interceptors/exception.interceptor.ts`) which check if the error is an instance of `ExceptionBase` and if so do the same has the `publishError()` function from above.
