DEV Community

Cover image for Reject a Schema-Invalid JSON Body Before API Gateway Calls a Backend
miruky
miruky

Posted on

Reject a Schema-Invalid JSON Body Before API Gateway Calls a Backend

Introduction

Hi, I'm miruky.

A backend should still enforce its own business rules, but it does not need to receive every structurally invalid request. Amazon API Gateway REST APIs can validate a request body against a JSON Schema model before proceeding with the integration request.

This Console run creates a POST /orders method with a mock integration. A body missing the required quantity property returns 400 from API Gateway, while a valid body continues through the mock integration and returns 200. No Lambda function, HTTP endpoint, deployment stage, or public invoke URL is required.

The exercise creates one REST API and uses only Console test invocations. API Gateway request pricing can change and differs by API type and Region, so check the current pricing page before using the pattern for production traffic.

1. Create a Regional REST API

API Gateway resources are Regional, so this run keeps the API and its Console tests in us-east-1. The opening screenshot establishes the English Console and Regional context before any API resources are added.

The English AWS Console shows United States (N. Virginia) before opening API Gateway.

The header confirms United States (N. Virginia) while the API Gateway Console is in English. This fixes the Regional context before any API resources are added.

Open API Gateway, choose APIs, and search for the exact generated name miruky-zazteywtwjosovgu. An empty exact-name result prevents this validation from colliding with an existing API.

The API Gateway list has no exact match for the generated REST API name.

The exact filter for miruky-zazteywtwjosovgu returns no API. That empty result establishes the resource boundary for the validation run.

Choose Create API, find REST API, and choose Build. Select New API, enter miruky-zazteywtwjosovgu, choose Regional as the endpoint type, and leave the description empty.

The REST API create form shows the generated name and Regional endpoint type.

The form visibly pairs miruky-zazteywtwjosovgu with the Regional endpoint type. No description or deployment-stage value is added.

After creation, open Resources and select the root resource. Do not deploy the API; the built-in method test can exercise the configuration without creating a stage or exposing an invoke URL.

The new REST API shows only its root resource before the orders method is added.

The resource tree contains only / at this point. The next step adds the request path without deploying the API.

2. Define the accepted JSON body

Open Models and choose Create model. Enter the alphanumeric generated name mirukytpnntggdocvcenbn, use application/json, and save this JSON Schema draft 4 model.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "required": ["product_id", "quantity"],
  "properties": {
    "product_id": {
      "type": "string"
    },
    "quantity": {
      "type": "integer",
      "minimum": 1
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The model create form contains the generated name, application/json, and the order schema.

The form shows mirukytpnntggdocvcenbn, application/json, and the complete schema together. Those values bind the model to the content type used by both test requests.

Open the saved model and inspect its schema. The model requires both properties and accepts only an integer of at least 1 for quantity.

The saved API Gateway model shows the required fields and quantity constraint.

The saved schema retains product_id, quantity, integer, and minimum as configured. That persisted model is the one attached to the method request later.

API Gateway models for REST APIs use JSON Schema draft 4, with documented feature limitations. Keep application-specific semantic validation in the backend even when the gateway checks the basic payload shape.

3. Add a POST method backed by a mock integration

Return to Resources, create the child resource /orders, and add a POST method. Choose Mock as the integration type so API Gateway itself can provide the successful response.

The POST orders method is created with the Mock integration type.

The method view visibly combines POST, /orders, and Mock. This keeps the accepted path self-contained inside API Gateway for the comparison.

On Integration request, add an application/json mapping template with this body. It selects the 200 integration response when a request passes method validation.

{"statusCode": 200}
Enter fullscreen mode Exit fullscreen mode

The mock integration request maps an accepted request to statusCode 200.

The request template contains {"statusCode": 200} for application/json. A request that reaches this integration is therefore routed to the success response.

On the default Integration response, add an application/json mapping template that returns a small synthetic response. The response contains no request data and does not echo any header or identifier.

{"accepted": true}
Enter fullscreen mode Exit fullscreen mode

The mock integration response returns a fixed accepted true body.

The response template contains {"accepted": true} for application/json. Its fixed body distinguishes the successful path from a gateway validation error.

Open Method request, choose Edit, and select Validate body as the request validator. Under Request body, add application/json and choose mirukytpnntggdocvcenbn as the model, then save.

The method request validates application/json bodies with the generated model.

The saved method request shows Validate body, application/json, and mirukytpnntggdocvcenbn together. The validator and model are now attached to the same content type used in the tests.

The content type matters. API Gateway performs body validation only when the request content type matches a configured model; a production API must define the intended behavior for other content types as well.

4. Compare a rejected body with an accepted body

Open the method's Test tab and submit this syntactically valid JSON object. It is structurally invalid for the method because the required quantity property is absent.

{
  "product_id": "sku-001"
}
Enter fullscreen mode Exit fullscreen mode

The Console test request contains product_id but omits the required quantity property.

The request body visibly contains product_id but omits the required quantity property. It is valid JSON, which isolates the model's required-property check from JSON parsing.

Choose Test after entering the body, and keep the result pane open for the comparison.

The invalid body receives 400 and the test log records request-body validation failure.

The result shows 400 and {"message": "Invalid request body"}. This gateway-generated response confirms that the request stopped at body validation.

The execution log identifies the missing required quantity property and completes with status 400.

The log says Request body does not match model schema for content type application/json, then identifies quantity inside the missing-required-properties message. It ends with Method completed with status: 400, tying the validation failure to the result above.

Now submit a body that satisfies the same model. The quantity value is an integer and meets the schema's minimum.

{
  "product_id": "sku-001",
  "quantity": 2
}
Enter fullscreen mode Exit fullscreen mode

The valid Console test request contains both required properties with valid types.

The valid body visibly contains both product_id and quantity, with the latter set to 2. It differs from the rejected body only in the required field under test.

Choose Test again without changing the method validator, model, or mock templates.

The valid body reaches the mock integration and returns the fixed 200 response.

The accepted result shows 200 and {"accepted": true}. This fixed body is the response configured on the mock integration path.

The valid-request log records successful request validation and the mapped response body.

The log says Request validation succeeded for content type application/json and then shows Method response body after transformations. It ends with Method completed with status: 200, matching the successful result above.

Wrap-up

The comparison separates two outcomes at the API boundary. The missing-property body stopped with a gateway-generated HTTP 400, while the model-compliant body proceeded to the integration and received its fixed HTTP 200 response.

Basic request validation is useful for required fields, types, and supported JSON Schema constraints. Authorization, cross-field business rules, database state, and other application semantics still belong in the appropriate backend controls.

Thanks for reading this far.

See you in the next one.

Disclosure: This article was written with AI assistance and independently verified against the linked primary sources and observed results.

References

Top comments (0)