AWS Lambda(Python)のバリデーションロジックについて

## はじめに

皆さんは、AWS Lambdaの開発言語は何を使っていますか？
選択肢はいろいろあるかと思いますが、私はPythonで開発しています。
Lambdaの中で送られてきた値のチェック(バリデーション)をすることも多いかと思いますが、そのロジック自前で実装していませんか？
ライブラリを使うと自前で実装せずに実装できるので紹介します。

## 実装例

### ソースコード

とにもかくにも、コードを見てもらったほうが早いと思います。

```py
from aws_lambda_powertools.utilities.validation import validate
from aws_lambda_powertools.utilities.validation.exceptions import SchemaValidationError

BODY_SCHEMA = {
    "$schema": "http://json-schema.org/draft-07/schema",
    "type": "object",
    "examples": [
        {"message": "hello world", "username": "lessa", "userArray": [{"name": "taro"}]}
    ],
    "required": ["message", "username", "userArray"],
    "properties": {
        "message": {
            "type": "string",
            "minLength": 5,
            "maxLength": 100,
        },
        "username": {
            "type": "string",
            "minLength": 5,
            "maxLength": 100,
        },
        "userArray": {
            "type": "array",
            "minItems": 1,
            "items": {
                "type": "object",
                "properties": {
                    "name": {"type": "string", "maxLength": 20, "minLength": 0},
                },
                "required": ["name"],
            },
        },
    },
}


def handler(event, _context):
    print(event)
    try:
        validate(event=event, schema=BODY_SCHEMA)
    except SchemaValidationError as e:
        print("error")
        print(e)

    return "Return value"


event = {"message": "aaaaa", "username": "lessa", "userArray": [{"name": "aaa"}]}
handler(event, {})
```

ソースコードの内容を順番に開設します。

### Lambda Powertools Python

今回利用するライブラリはLambda Powertools Pythonです。
[Lambda Powertools Python](https://awslabs.github.io/aws-lambda-powertools-python/latest/)
Lambda Powertools Pythonは、今回利用するValidationだけでなく、TracerやLoggerもとても便利ですので、興味があればぜひ調べてみてください。
今回は[Validation](https://awslabs.github.io/aws-lambda-powertools-python/latest/utilities/validation/)を使います。

インストール方法は以下になります。

```sh
$ pip install aws-lambda-powertools
```

Validationの使い方として、デコレータ方法と関数を使う方法の2通りがあります。どちらを使ってもいいのですが、個人的には関数で利用するほうがおすすめです。
今回はバリデーションでエラーになった場合に処理を分岐させるので、都合がいいからです。

### JSON Schemaについて

型の定義はJSON Schemaという形式で記載します。
Swagger記法と同じような書き方ですね。
内容はそこまで難しくないと思いますので、参考リンクを記載しておきます。

* [JSON Schema](https://json-schema.org/)
* [JSON Schemaのすゝめ](https://qiita.com/g0e/items/9a4f886897fd46f107a8)

```py
BODY_SCHEMA = {
    "$schema": "http://json-schema.org/draft-07/schema",
    "type": "object",
    "examples": [
        {"message": "hello world", "username": "lessa", "userArray": [{"name": "taro"}]}
    ],
    "required": ["message", "username", "userArray"],
    "properties": {
        "message": {
            "type": "string",
            "minLength": 5,
            "maxLength": 100,
        },
        "username": {
            "type": "string",
            "minLength": 5,
            "maxLength": 100,
        },
        "userArray": {
            "type": "array",
            "minItems": 1,
            "items": {
                "type": "object",
                "properties": {
                    "name": {"type": "string", "maxLength": 20, "minLength": 0},
                },
                "required": ["name"],
            },
        },
    },
}
```

### Validation実行

Validationの実行部分もそこまで難しくないと思います。
第一引数に検査したいオブジェクトを入れ、第二引数にスキーマを入れます。
バリデーションが失敗した場合は、SchemaValidationErrorが返ります。

```py
def handler(event, _context):
    print(event)
    try:
        validate(event=event, schema=BODY_SCHEMA)
    except SchemaValidationError as e:
        print("error")
        print(e)

    return "Return value"


event = {"message": "aaaaa", "username": "lessa", "userArray": [{"name": "aaa"}]}
handler(event, {})
```

ちなみに、エラーとなった場合は以下のように出力されます。
messageは5文字以上、100文字以内と定義しているので、3文字で入力したのでエラーとなっています。

```
{'message': 'aaa', 'username': 'lessa', 'userArray': [{'name': 'aaa'}]}
error
Failed schema validation. Error: data.message must be longer than or equal to 5 characters, Path: ['data', 'message'], Data: aaa
```

## 運用方法とメリット

Lambda Powertools PythonのTracerやLoggerは必須ツールなので、導入されている場合も多いかと思います。
Lambda Powertools PythonがLambda Layerなどを通して利用可能であれば、特に追加の設定等は不要でValidationの関数を利用可能です。
特にAPIのリクエスト内容に関してのバリデーションは必須ロジックとなっているので、自前で実装するよりは今回紹介した方法でより開発スピードと品質の向上が期待できるのではないでしょうか。