Basic Server
This example demonstrates the simplest way to create a GraphQL server with Effect GQL. It includes basic queries, mutations, and the GraphiQL playground.
Running the Example
Section titled “Running the Example”pnpm example:basic# Server starts at http://localhost:4000What You’ll Learn
Section titled “What You’ll Learn”- Setting up a basic GraphQL schema with Effect GQL
- Defining queries with and without arguments
- Creating mutations that modify data
- Serving the schema with GraphiQL enabled
Complete Code
Section titled “Complete Code”import { Effect, Layer } from "effect"import * as S from "effect/Schema"import { HttpRouter, HttpServerResponse } from "@effect/platform"import { GraphQLSchemaBuilder, query, mutation, makeGraphQLRouter } from "@effect-gql/core"import { serve } from "@effect-gql/node"
// Domain model defined with Effect Schemaconst User = S.Struct({ id: S.String, name: S.String, email: S.String,})
type User = S.Schema.Type<typeof User>
// In-memory data storeconst users: User[] = [ { id: "1", name: "Alice", email: "alice@example.com" }, { id: "2", name: "Bob", email: "bob@example.com" },]
// Build the GraphQL schemaconst schema = GraphQLSchemaBuilder.empty .pipe( // Simple query query("hello", { type: S.String, resolve: () => Effect.succeed("Hello from Effect GQL!"), }),
// Query with arguments query("users", { type: S.Array(User), resolve: () => Effect.succeed(users), }),
// Query with optional return query("user", { args: S.Struct({ id: S.String }), type: S.OptionFromNullOr(User), resolve: (args) => Effect.succeed(users.find((u) => u.id === args.id) ?? null), }),
// Mutation mutation("createUser", { args: S.Struct({ name: S.String, email: S.String }), type: User, resolve: (args) => Effect.sync(() => { const newUser: User = { id: String(users.length + 1), name: args.name, email: args.email, } users.push(newUser) return newUser }), }) ) .buildSchema()
// Create the HTTP router with GraphQL endpointconst graphqlRouter = makeGraphQLRouter(schema, Layer.empty, { path: "/graphql", graphiql: { path: "/graphiql", endpoint: "/graphql", },})
// Compose with other routesconst app = HttpRouter.empty.pipe( HttpRouter.get("/health", HttpServerResponse.json({ status: "ok" })), HttpRouter.concat(graphqlRouter))
// Start the serverserve(app, Layer.empty, { port: 4000, onStart: (url) => { console.log(`Server ready at ${url}`) console.log(`GraphiQL: ${url}/graphiql`) },})Key Concepts
Section titled “Key Concepts”Effect Schema as Source of Truth
Section titled “Effect Schema as Source of Truth”The User schema defines both the TypeScript type and the GraphQL type:
const User = S.Struct({ id: S.String, name: S.String, email: S.String,})
// TypeScript type is inferredtype User = S.Schema.Type<typeof User>Pipe API
Section titled “Pipe API”The pipe method allows fluent composition of schema elements:
GraphQLSchemaBuilder.empty.pipe( query("hello", { ... }), query("users", { ... }), mutation("createUser", { ... }))Effect-based Resolvers
Section titled “Effect-based Resolvers”All resolvers return Effect values, enabling composition with other Effects:
resolve: (args) => Effect.succeed(users.find((u) => u.id === args.id) ?? null)Example Queries
Section titled “Example Queries”Try these in GraphiQL:
# Simple queryquery { hello}
# Get all usersquery { users { id name email }}
# Get user by IDquery { user(id: "1") { name email }}
# Create a new usermutation { createUser(name: "Charlie", email: "charlie@example.com") { id name }}Next Steps
Section titled “Next Steps”- DataLoaders Example - Learn to solve the N+1 problem
- Subscriptions Example - Add real-time updates
- Full-Featured Example - Production patterns