A GraphQL API that Does Not Publish Its Own Schema
introspection_config defaults to ENABLED, handing any caller a complete map of every type, field and argument, and query_depth_limit defaults to 0, which means no limit - so one nested query multiplies into thousands of resolver calls. Both closed here, with request bodies kept out of CloudWatch and a WAF association for the rate nothing else bounds.
Verification
Static-verifiedPassed: validated and lint-clean (provider-schema-validated for AWS/Azure/GCP; Terraform-language lint elsewhere).
Conformance
- Static validation (fmt · validate · tflint)
- Security scan clean (Checkov)
- Plan tests (mocked: validation rules · outputs)
Provenance
- SHA-256 checksum
- Signature (pending)
Functional
- Live test pending (no cloud run yet)
Last verified 2026-09-13 · how we verify
Use it from the registry
terraform · opentofumodule "appsync" {
source = "www.iac-bazaar.com/iac-bazaar/aws-appsync/aws"
version = "1.0.0"
}Needs a registry token from /account/tokens. The module itself is free; the account is what identifies you. Full setup: registry docs.
Inputs & outputs
Create a free account to read this module's contract
The declared contract - every input name, type, default and description, plus every output - is shown to signed-in accounts, not to anonymous visitors.
A free account sees the contract of every module in the catalogue. There is no subscription and nothing to buy - the modules are free to download, and they run under Vizier.
Documentation
aws-appsync
An AppSync GraphQL API that does not publish its own schema, cannot be asked an
arbitrarily deep question, and does not write its callers' data into CloudWatch.
Works with Terraform and OpenTofu (>= 1.6), AWS provider >= 6.0, < 7.0.
introspection_config defaults to ENABLED. GraphQL introspection returns
the whole schema - every type, every field, every argument, including the
internal ones and the ones you never documented. Anyone who can reach the API
with any credential can download a complete map of it. DISABLED here.
query_depth_limit defaults to 0, which means no limit. GraphQL schemas
are usually cyclic - an author has books, a book has an author - so a caller can
nest a query as deep as they like, and every level multiplies the resolver calls
beneath it. One request becomes thousands, against your resolvers and whatever
they talk to. 16 here, with resolver_count_limit bounded too.
Turning logging on without exclude_verbose_content writes request and
response bodies to CloudWatch. That is your callers' data, copied into a log
group with its own retention and its own access policy, as a side effect of
enabling observability. Excluded here; field_log_level still records what
happened. A precondition refuses the verbose combination unless
accept_verbose_logs says it was meant.
API_KEY is a bare bearer token - no identity, no attribution, and no
revocation short of deleting the key for everyone holding it. The module accepts
it and requires accept_api_key_auth to say so out loud. A second precondition
refuses an auth type whose configuration block is missing, which otherwise
creates an API nobody can authenticate against.
Enum-valued settings are exposed as the API's own strings rather than as booleans a conditional converts. That is deliberate: a rendered ternary reads as unresolved to configuration scanners, which then report a control as absent on a module that has it - verified by bisect twice while building this batch.
A depth limit bounds one query; nothing bounds the rate. query_depth_limit
makes a single request cheap, and an internet-facing GraphQL API with no rate
limit simply receives more of them. Rate-based rules live in WAF, so pass
web_acl_arn and the module associates a REGIONAL WAFv2 web ACL with the
API.
Smaller things: log_config is a static block and the module creates the
CloudWatch role when you do not pass one - needing to build an IAM role first is
how field-level logging ends up switched off in practice, and a dynamic block
here would also be unreadable to configuration scanners; the created role's
trust policy is scoped with aws:SourceAccount, so the shared
appsync.amazonaws.com principal cannot be used to assume it on behalf of
someone else's API; and app_id_client_regex is surfaced, since leaving it null
admits every app client in the pool including ones created later by somebody
else.
Verification
Static validation runs tofu fmt, init, validate, tflint and checkov.
This module has not yet had a live test, so it is published as statically
validated with its live test pending and does not carry the live-tested mark.
Usage code & full reference need an account
The complete copy-paste usage, the full input/output reference, and operational notes are free with an account - shown here and bundled in the download. Sign in and this section fills in.
- Usage
Related modules
aws-apigateway-http
HTTP API with routes, Lambda/ALB integrations, custom domain, JWT authorizers, and access logs.
aws-apigateway-rest
A REST API wired end to end - resource tree built from route paths, deny-by-default IAM authorization, MOCK/Lambda/HTTP integrations, deployment + stage with throttling and JSON access logs.