1import type * as Credentials from "@distilled.cloud/aws/Credentials";
2import type * as SigV4 from "@distilled.cloud/aws/SigV4";
3import * as Data from "effect/Data";
4import type * as Effect from "effect/Effect";
5import * as Binding from "../../Binding.ts";
6import type { GraphqlApi } from "./GraphqlApi.ts";
7
8/**
9 * The GraphQL endpoint returned a non-2xx response or an unparseable body.
10 * GraphQL *field* errors do NOT fail the effect — they are surfaced on
11 * {@link GraphQLResult.errors} alongside any partial `data`.
12 */
13export class GraphQLApiError extends Data.TaggedError("GraphQLApiError")<{
14 /** HTTP status of the failed request (0 when the request never left). */
15 readonly status: number;
16 /** The raw response body (or the underlying failure message). */
17 readonly body: string;
18}> {}
19
20/** A single entry of the standard GraphQL `errors` array. */
21export interface GraphQLErrorItem {
22 message: string;
23 path?: ReadonlyArray<string | number>;
24 locations?: ReadonlyArray<{ line: number; column: number }>;
25 errorType?: string;
26 [key: string]: unknown;
27}
28
29/** The standard GraphQL response envelope. */
30export interface GraphQLResult<T = unknown> {
31 /** The (possibly partial) result data. */
32 data?: T;
33 /** GraphQL field/validation errors, if any. */
34 errors?: ReadonlyArray<GraphQLErrorItem>;
35 extensions?: Record<string, unknown>;
36}
37
38/** A GraphQL operation to execute against the API. */
39export interface GraphQLRequest {
40 /** The GraphQL document, e.g. `query($a: Int!) { add(a: $a, b: 1) }`. */
41 query: string;
42 /** Values for the document's variables. */
43 variables?: Record<string, unknown>;
44 /** Which operation to run when the document defines several. */
45 operationName?: string;
46}
47
48export interface GraphQLClient {
49 /**
50 * Execute a GraphQL operation (query or mutation), SigV4-signed with the
51 * host Function's credentials. Fails only on transport/HTTP errors —
52 * GraphQL field errors are returned on `result.errors`.
53 */
54 execute<T = unknown>(
55 request: GraphQLRequest,
56 ): Effect.Effect<
57 GraphQLResult<T>,
58 GraphQLApiError | Credentials.CredentialsError | SigV4.SigningError
59 >;
60}
61
62/**
63 * Runtime binding for the `appsync:GraphQL` data-plane action — execute
64 * GraphQL operations against a {@link GraphqlApi}'s endpoint from a Lambda
65 * (or other AWS runtime), SigV4-signed with the host Function's IAM role.
66 *
67 * The API must accept `AWS_IAM` authentication (as its primary mode or an
68 * additional provider). Provide `AppSync.GraphQLHttp` on the hosting
69 * function's Effect to implement the binding.
70 *
71 * ### Executing GraphQL Operations
72 * **Example:** Query an IAM-authenticated API
73 * ```typescript
74 * const api = yield* AppSync.GraphqlApi("Api", {
75 * authenticationType: "AWS_IAM",
76 * schema,
77 * });
78 * const graphql = yield* AppSync.GraphQL(api);
79 *
80 * const result = yield* graphql.execute<{ add: number }>({
81 * query: "query($a: Int!, $b: Int!) { add(a: $a, b: $b) }",
82 * variables: { a: 2, b: 3 },
83 * });
84 * // result.data?.add === 5; field errors appear on result.errors
85 * ```
86 *
87 * @binding
88 */
89export interface GraphQL extends Binding.Service<
90 GraphQL,
91 "AWS.AppSync.GraphQL",
92 (api: GraphqlApi) => Effect.Effect<GraphQLClient>
93> {}
94export const GraphQL = Binding.Service<GraphQL>("AWS.AppSync.GraphQL");