GraphQL Package
The @hazeljs/graphql package provides GraphQL server and client support for HazelJS applications. It offers a simplified decorator-based API for building schemas, resolvers, and typed clients—powered by graphql and graphql-http.
Purpose
GraphQL APIs typically require schema definitions, resolver wiring, and HTTP handling. The @hazeljs/graphql package simplifies this by providing:
- Decorator-Based Schema: Use
@Resolver,@Query,@Mutation,@ObjectType,@Field, and@Argto define your API - Automatic HTTP Handling: GraphQL endpoint served at a configurable path (default
/graphql) with GraphQL over HTTP - DI Integration: Resolvers are resolved from the HazelJS container—inject services and other providers
- Typed Client:
GraphQLClientfor executing queries and mutations with optional decorators - Module Pattern: Familiar
GraphQLModule.forRoot()configuration
Architecture
The package uses decorator metadata to build a GraphQL schema and wires it to an HTTP handler:
graph TD A["@Resolver, @Query, @Mutation<br/>(Decorators)"] --> B["GraphQLModule.forRoot()<br/>(Module Configuration)"] B --> C["SchemaBuilder<br/>(Builds Schema from Decorators)"] C --> D["GraphQLServer<br/>(graphql-http Handler)"] D --> E["Early Handler<br/>(/graphql on HTTP Server)"] F["@ObjectType, @Field<br/>(Optional Object Types)"] --> C style A fill:#8b5cf6,stroke:#a78bfa,stroke-width:2px,color:#fff style B fill:#3b82f6,stroke:#60a5fa,stroke-width:2px,color:#fff style C fill:#10b981,stroke:#34d399,stroke-width:2px,color:#fff style D fill:#3b82f6,stroke:#60a5fa,stroke-width:2px,color:#fff style E fill:#f59e0b,stroke:#fbbf24,stroke-width:2px,color:#fff
Key Components
- GraphQLModule: Configures the GraphQL server with path and resolvers
- GraphQLServer: Builds schema from decorators and serves via graphql-http
- SchemaBuilder: Converts
@Resolver,@Query,@Mutationmetadata into a GraphQL schema - GraphQLClient: Typed client for queries and mutations
- Decorators:
@Resolver,@Query,@Mutation,@ObjectType,@Field,@Argfor server;@GraphQLClientClass,@GraphQLQuery,@GraphQLMutationfor client
Advantages
1. Code-First Schema
Define your API with TypeScript classes and decorators—no separate SDL files to maintain.
2. Decorator-Based Resolvers
Use @Query() and @Mutation() to declare handlers—clean and aligned with HazelJS patterns.
3. Full DI Integration
Resolvers are resolved from the HazelJS container—inject services, repositories, and other providers.
4. Integrated HTTP
GraphQL is served on the same HTTP server as your REST API, via an early handler before body parsing.
5. Typed Client
GraphQLClient for executing queries and mutations with optional decorator-based client classes.
Installation
npm install @hazeljs/graphql @hazeljs/core
Quick Start
1. Create Resolvers
import { Injectable } from '@hazeljs/core';
import { Resolver, Query, Mutation, Arg } from '@hazeljs/graphql';
@Injectable()
@Resolver()
export class UserResolver {
@Query()
hello() {
return 'Hello, GraphQL!';
}
@Query()
user(@Arg('id') id: string) {
return { id, name: `User ${id}` };
}
@Mutation()
createUser(@Arg('name') name: string) {
return { id: '1', name };
}
}
2. Register GraphQL Module
import { HazelModule } from '@hazeljs/core';
import { GraphQLModule } from '@hazeljs/graphql';
import { UserResolver } from './user.resolver';
@HazelModule({
imports: [
GraphQLModule.forRoot({
path: '/graphql',
resolvers: [UserResolver],
}),
],
})
export class AppModule {}
3. Start the App
import { HazelApp } from '@hazeljs/core';
const app = new HazelApp(AppModule);
app.listen(3000);
// GraphQL available at http://localhost:3000/graphql
Configuration
Configure the GraphQL module via GraphQLModule.forRoot():
GraphQLModule.forRoot({
path: '/graphql', // Endpoint path (default: /graphql)
resolvers: [UserResolver, PostResolver], // Resolver classes
playground: true, // Enable GraphiQL in development (optional)
introspection: true, // Enable introspection (default: true)
});
Object Types (Optional)
For complex return types, use @ObjectType and @Field:
import { ObjectType, Field } from '@hazeljs/graphql';
@ObjectType('User')
class User {
@Field()
id!: string;
@Field()
name!: string;
@Field('emailAddress')
email!: string;
}
Decorators Reference
Server Decorators
| Decorator | Description |
|---|---|
@Resolver(name?) | Marks a class as a GraphQL resolver |
@Query(name?) | Marks a method as a Query field |
@Mutation(name?) | Marks a method as a Mutation field |
@Arg(name, type?) | Marks a parameter as a GraphQL argument |
@ObjectType(name?) | Marks a class as a GraphQL object type |
@Field(name?) | Marks a property or method as a GraphQL field |
Client Decorators
| Decorator | Description |
|---|---|
@GraphQLClientClass(url, headers?) | Marks a class as a GraphQL client |
@GraphQLQuery() | Marks a method as a query executor |
@GraphQLMutation() | Marks a method as a mutation executor |
GraphQL Client
Use GraphQLClient for typed queries and mutations:
import { GraphQLClient } from '@hazeljs/graphql';
const client = new GraphQLClient({
url: 'http://localhost:3000/graphql',
headers: { Authorization: 'Bearer token' },
});
// Query
const data = await client.query(`
query {
hello
user(id: "1") { id name }
}
`);
// Mutation
const result = await client.mutate(`
mutation {
createUser(name: "Alice") { id name }
}
`);
Complete Example
// user.resolver.ts
import { Injectable } from '@hazeljs/core';
import { Resolver, Query, Mutation, Arg } from '@hazeljs/graphql';
@Injectable()
@Resolver()
export class UserResolver {
@Query()
hello() {
return 'Hello, GraphQL!';
}
@Query()
user(@Arg('id') id: string) {
return { id, name: `User ${id}` };
}
@Mutation()
createUser(@Arg('name') name: string) {
return { id: Date.now().toString(), name };
}
}
// app.module.ts
import { HazelModule } from '@hazeljs/core';
import { GraphQLModule } from '@hazeljs/graphql';
import { UserResolver } from './user.resolver';
@HazelModule({
imports: [
GraphQLModule.forRoot({
path: '/graphql',
resolvers: [UserResolver],
}),
],
})
export class AppModule {}
// main.ts
import { HazelApp } from '@hazeljs/core';
const app = new HazelApp(AppModule);
app.listen(3000);
Best Practices
-
Use dependency injection: Inject services and repositories into resolvers—they're regular HazelJS providers.
-
Return strings for scalar fields: When the schema infers
String, return string values. For objects, considerJSON.stringify()or define proper@ObjectTypeclasses. -
Keep resolvers focused: One resolver per domain (e.g.,
UserResolver,PostResolver). -
Use @Arg for arguments: Always annotate parameters with
@Arg('name')for GraphQL to map them correctly. -
Test with the client: Use
GraphQLClientor tools like GraphiQL to verify your API.