Make useful JS/TS errors in AWS Lambda
Tags: typescript, logs, lambda
Published: 2020-07-31 12:10:56

---
title: Make useful JS/TS errors in AWS Lambda
tags: typescript, logs, lambda
series: logging and monitoring
---

I have been playing with custom errors and how it can help with monitoring AWS lambda based APIs.

The native Error of JavaScript is kinda meh. You can only set a message, which, I find, is not enough to determine an issue in production, i.e. ‘http request failed’. What I would love to know is some context, at least: url, headers, payload, response.

I have tried to stringify error context and attach to an error message, but it was difficult to read the logs and therefore didn't spark joy. 

Ultimately, I would prefer not to look at the logs at all, the monitoring tool should tell me what's wrong and maybe even give a solution. Got better things to do, than debugging logs :)))


## Error does not like serialisation.

```
try {
  throw new Error("Something bad happened");
} catch (err) {
  const serialised = JSON.stringify(err);
  console.log(serialised);
}
```
outputs:
```
{}
```

## Let's serialise the Error
So what we could do is to create an BaseError, which has a property reason.

```
export class BaseError extends Error {
    public readonly reason: string;

    constructor(reason: string) {
        super(reason);
        this.name = BaseError.name;
        Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain

        this.reason = reason;
    }
}
```

now when we run this:

```
try {
  throw new BaseError("Something bad happened");
} catch (err) {
  let serialised = JSON.stringify(err);
  console.log(serialised);
}
```

we get

```
{"name":"BaseError","reason":"Something bad happened"}
```

## Let's make a custom Error

So once we have a BaseError, we can create an purpose built error like that:

```
export class SuperHeroError extends BaseError {
  public readonly superHeroName: string;

  constructor(superHeroName: string) {
    super(`superhero ${superHeroName} malfunctioned`);
    this.name = SuperHeroError.name;
    Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain

    this.superHeroName = superHeroName;
  }
}
```

and when we run it:

```
try {
  throw new SuperHeroError("Batman");
} catch (err) {
  let serialised = JSON.stringify(err);
  console.log(serialised);
}
```

we get this:

```
{"name":"SuperHeroError","reason":"superhero Batman malfunctioned","superHeroName":"Batman"}
```

## AWS CloudWatch LogInsights report
Once we have some failure logs, we can write a query like this:

```
fields @timestamp as timestamp, name, reason
| sort @timestamp desc
| limit 100
```

and the result will be:
-----------------------------------------------------------------------------
|       timestamp        |      name      |             reason             |
|-------------------------|----------------|--------------------------------|
| 2020-07-23 12:59:30.716 | SuperHeroError | superhero Batman malfunctioned |
-----------------------------------------------------------------------------

This is very basic error logging, in the real world you will need much more data in the logs, like sessionId, requestId etc. :)