HazelJS GraphQL Package
@hazeljs/graphql provides decorator-based GraphQL server and client support for HazelJS, powered by graphql and graphql-http.
Quick Reference
- Purpose:
@hazeljs/graphqlprovides decorator-based GraphQL schema definition, resolvers, and typed clients for HazelJS applications. - When to use: Use
@hazeljs/graphqlwhen building a GraphQL API instead of REST. Use@hazeljs/corecontrollers for REST APIs. - Key concepts:
@Resolver(),@Query(),@Mutation(),@ObjectType(),@Field(),@Arg()decorators,GraphQLModule, typed GraphQL client. - Dependencies:
@hazeljs/core,graphql,graphql-http. - Common patterns: Define types with
@ObjectType()and@Field()→ create resolvers with@Resolver()→ add queries with@Query()and mutations with@Mutation()→ registerGraphQLModule. - Common mistakes: Not adding
@Field()to all type properties (missing from schema); mixing REST and GraphQL on the same endpoint path; not handling N+1 query problem with DataLoaders.
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 { Service } from '@hazeljs/core';
import { Resolver, Query, Mutation, Arg } from '@hazeljs/graphql';
@Service()
@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 { Service } from '@hazeljs/core';
import { Resolver, Query, Mutation, Arg } from '@hazeljs/graphql';
@Service()
@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.
What's Next?
- Explore WebSocket for real-time subscriptions (GraphQL subscriptions can be added separately)
- Check out Swagger for REST API documentation alongside GraphQL
- Learn about Discovery for microservice coordination
Recipes
Recipe: GraphQL Query and Mutation
// File: src/users/users.resolver.ts
import { Resolver, Query, Mutation, Args } from '@hazeljs/graphql';
import { Service } from '@hazeljs/core';
@Resolver('User')
@Service()
export class UsersResolver {
private users = [{ id: '1', name: 'Alice', email: 'alice@example.com' }];
@Query('users')
findAll() {
return this.users;
}
@Query('user')
findOne(@Args('id') id: string) {
return this.users.find(u => u.id === id);
}
@Mutation('createUser')
create(@Args('name') name: string, @Args('email') email: string) {
const user = { id: String(this.users.length + 1), name, email };
this.users.push(user);
return user;
}
}
Recipe: Register GraphQL Module
// File: src/app.module.ts
import { HazelModule } from '@hazeljs/core';
import { GraphQLModule } from '@hazeljs/graphql';
import { UsersResolver } from './users/users.resolver';
@HazelModule({
imports: [
GraphQLModule.register({
autoSchemaFile: true,
playground: true,
path: '/graphql',
}),
],
providers: [UsersResolver],
})
export class AppModule {}
Related Resources
- WebSocket Package – Real-time subscriptions
- Swagger Package – REST API documentation
- Discovery Package – Microservice coordination
- Prisma Package – Database integration
- TypeORM Package – Alternative database ORM