Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

GraphQL

Typed GraphQL operations for GraphQLGroup and GraphQLClient.make, written by hand or generated by @effect/graphql-generator.

Includes the generator's lenient decoders, otherTypename and enumLiterals.

17 exports Since v4.0.0 Source

Constructors

Since v4.0.0

Defines a mutation. Same shape as query.

Signature

declare const mutation: <Name extends string, Result extends Schema.Top, Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}>>(name: Name, options: {
readonly document: string | DocumentTemplate;
readonly result: Result;
readonly variables?: Variables;
}) => Operation<"mutation", Name, Variables extends Schema.Struct.Fields ? Schema.Struct<Variables> : Variables, Result>

Example A mutation

import { Schema } from "effect"
import { GraphQL } from "effect/graphql"
const AddComment = GraphQL.mutation("AddComment", {
document: "mutation AddComment($subjectId:ID!,$body:String!){addComment(input:{subjectId:$subjectId,body:$body}){clientMutationId}}",
variables: { subjectId: Schema.String, body: Schema.String },
result: Schema.Struct({ addComment: Schema.NullOr(Schema.Struct({ clientMutationId: Schema.NullOr(Schema.String) })) })
})
AddComment.kind // => "mutation"
Since v4.0.0

Defines a query. The client accepts decoded variables and encodes them through their Schema, including custom scalar codecs. variables accepts struct fields or a Schema; omit it for an operation without variables. document accepts printed text or a DocumentTemplate, joined once when the operation is defined.

Signature

declare const query: <Name extends string, Result extends Schema.Top, Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}>>(name: Name, options: {
readonly document: string | DocumentTemplate;
readonly result: Result;
readonly variables?: Variables;
}) => Operation<"query", Name, Variables extends Schema.Struct.Fields ? Schema.Struct<Variables> : Variables, Result>

Example A query with variables

import { Schema } from "effect"
import { GraphQL } from "effect/graphql"
const RepoIssues = GraphQL.query("RepoIssues", {
document: "query RepoIssues($owner:String!,$name:String!){repository(owner:$owner,name:$name){issues(first:10){nodes{number title}}}}",
variables: { owner: Schema.String, name: Schema.String },
result: Schema.Struct({
repository: Schema.NullOr(Schema.Struct({
issues: Schema.Struct({
nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Schema.Struct({ number: Schema.Int, title: Schema.String }))))
})
}))
})
})
RepoIssues.kind // => "query"
RepoIssues.name // => "RepoIssues"

Defines a subscription. The client method for a subscription returns a Stream of decoded events instead of an Effect.

Signature

declare const subscription: <Name extends string, Result extends Schema.Top, Variables extends Schema.Top | Schema.Struct.Fields = Schema.Struct<{}>>(name: Name, options: {
readonly document: string | DocumentTemplate;
readonly result: Result;
readonly variables?: Variables;
}) => Operation<"subscription", Name, Variables extends Schema.Struct.Fields ? Schema.Struct<Variables> : Variables, Result>

Example A subscription

import { Schema } from "effect"
import { GraphQL } from "effect/graphql"
const IssueUpdated = GraphQL.subscription("IssueUpdated", {
document: "subscription IssueUpdated($id:ID!){issueUpdated(id:$id){title}}",
variables: { id: Schema.String },
result: Schema.Struct({ issueUpdated: Schema.Struct({ title: Schema.String }) })
})
IssueUpdated.kind // => "subscription"

Guards

Tests whether a value is an Operation.

Signature

declare function isOperation(u: unknown): u is Any

Models

Since v4.0.0

An operation document split into the pieces needed to rename and alias it without a GraphQL parser. The generator emits one for every query and mutation.

Details

A body is a DocumentTemplate.Parts array: literal text at even indices and, at odd indices, the variable names and fragment names that would be renamed if the operation were repeated under a prefix. Joining the pieces gives the printed document, which is what the operation sends.

A fragment whose body has a name in it is per-slice: it references a variable, directly or through a fragment it spreads. Every other fragment is shared.

Signature

interface DocumentTemplate {
readonly directives?: Parts;
readonly fragments: readonly Array<Fragment>;
readonly selections: readonly Array<Selection>;
readonly variables: readonly Array<Variable>;
}

Kind

type
Since v4.0.0

The three kinds of GraphQL operation.

Signature

type Kind = "query" | "mutation" | "subscription"

Operation

interface
Since v4.0.0

A named GraphQL document with variable and result schemas and middleware. Variables are encoded before sending; results are decoded from data.

Signature

interface Operation<out K extends Kind, out Name extends string, out Variables extends Schema.Top, out Result extends Schema.Top, out Middleware = never> extends Pipeable {
readonly "~effect/graphql/GraphQL": "~effect/graphql/GraphQL";
readonly "~middleware"?: Middleware;
readonly document: string;
readonly kind: K;
readonly middlewares: readonly Array<AnyService>;
readonly name: Name;
readonly result: Result;
readonly template: DocumentTemplate | undefined;
readonly variables: Variables;
middleware<M extends AnyService>(middleware: M): Operation<K, Name, Variables, Result, Middleware | M>;
}

Other

The pieces of a DocumentTemplate.

Schemas

The Schema for a GraphQL enum in a result position.

Details

It is typed as the declared literals but decodes any string, so a value the server adds later does not fail the whole query. Inputs stay strict: the generator uses Schema.Literals for enums in variables and input objects.

Signature

declare function enumLiterals<Literals extends readonly Array<string>>(literals: Literals): refine<Literals[number], String>

Example An enum that tolerates new values

import { Schema } from "effect"
import { GraphQL } from "effect/graphql"
const IssueState = GraphQL.enumLiterals(["OPEN", "CLOSED"])
Schema.decodeUnknownSync(IssueState)("OPEN") // => "OPEN"
Schema.decodeUnknownSync(IssueState)("MERGED") // => "MERGED"
Schema.is(IssueState)(42) // => false

The __typename Schema for the "every other type" member of a generated union.

Details

The type excludes selected names, allowing __typename narrowing. The decoder accepts any other string, including types added by the server later. Selected names are rejected so malformed selected results cannot fall through to the catch-all member.

Signature

declare function otherTypename<All extends string>(): <Selected extends readonly Array<All>>(selected: Selected) => refine<Exclude<All, Selected[number]>, String>

Example The catch-all member of a union

import { Schema } from "effect"
import { GraphQL } from "effect/graphql"
type Node = "Issue" | "PullRequest" | "Repository"
const OtherNode = Schema.Struct({
__typename: GraphQL.otherTypename<Node>()(["Issue"]),
id: Schema.String
})
Schema.decodeUnknownSync(OtherNode)({ __typename: "PullRequest", id: "1" }).__typename // => "PullRequest"
Schema.decodeUnknownSync(OtherNode)({ __typename: "AddedLater", id: "2" }).__typename // => "AddedLater"
Schema.is(OtherNode)({ __typename: "Issue", id: "3" }) // => false

Utility Types

Since v4.0.0

The type of an operation after attaching middleware M.

Signature

type AddMiddleware<Op, M> = Op extends Operation<infer K, infer N, infer V, infer R, infer Old> ? Operation<K, N, V, R, Old | M> : never

Any

type
Since v4.0.0

Any operation, with its type parameters erased.

Signature

type Any = Operation<Kind, string, Schema.Top, Schema.Top, any>

Error

type
Since v4.0.0

The error type of a client method for an operation: GraphQLClientError plus the errors of every attached middleware.

Signature

type Error<Op extends Any> = GraphQLClientError | GraphQLMiddleware.Error<Middleware<Op>>
Since v4.0.0

Extracts the union of middleware attached to an operation.

Signature

type Middleware<Op> = Op extends Operation<any, any, any, any, infer M> ? M : never
Since v4.0.0

The decoded result type of an operation.

Signature

type Result<Op extends Any> = Op["result"]["Type"]
Since v4.0.0

The services a client method for an operation requires: the Schemas' encoding and decoding services plus the requirements of every attached middleware.

Signature

type Services<Op extends Any> = Op["variables"]["EncodingServices"] | Op["result"]["DecodingServices"] | GraphQLMiddleware.Requires<Middleware<Op>>
Since v4.0.0

The decoded variables type of an operation, which is what a client method takes.

Signature

type Variables<Op extends Any> = Op["variables"]["Type"]