> ## Documentation Index
> Fetch the complete documentation index at: https://www.orionjs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Resolvers

> Creating GraphQL field resolvers for specific types in Orionjs

Model resolvers in Orionjs provide a way to define computed fields and relationships on your GraphQL types. Unlike regular resolvers which define root queries and mutations, model resolvers add fields to specific GraphQL types.

## Creating Model Resolvers

A model resolver controller is a class decorated with `@ModelResolvers()` that specifies the GraphQL type to extend, and contains methods decorated with `@ModelResolver()` to define fields on that type:

```typescript theme={null}
import { ModelResolver, ModelResolvers, createModelResolver } from '@orion-js/graphql'
import { Inject } from '@orion-js/services'
import { schemaWithName, InferSchemaType } from '@orion-js/schema'
import { PostService } from '../services/PostService'

// Define schemas using schemaWithName
export const UserSchema = schemaWithName('UserSchema', {
  _id: { type: String },
  name: { type: String },
  email: { type: String }
})

export const PostSchema = schemaWithName('PostSchema', {
  _id: { type: String },
  title: { type: String },
  content: { type: String },
  authorId: { type: String }
})

// Infer types from schemas
export type UserType = InferSchemaType<typeof UserSchema>
export type PostType = InferSchemaType<typeof PostSchema>

@ModelResolvers(UserSchema)
export default class UserResolvers {
  @Inject(() => PostService)
  private postService: PostService

  @ModelResolver()
  posts = createModelResolver<UserType>({
    returns: [PostSchema],
    resolve: async (user) => {
      return await this.postService.getUserPosts(user._id)
    }
  })
}
```

The `@ModelResolvers()` decorator takes the schema that represents your GraphQL type. Each method decorated with `@ModelResolver()` adds a new field to that type.

## Model Resolver Methods

Model resolver methods receive the parent object as their first parameter, followed by any parameters, the viewer, and GraphQL info:

```typescript theme={null}
@ModelResolver()
posts = createModelResolver<UserType>({
  params: PostFiltersParams,
  returns: [PostSchema],
  resolve: async (user, params, viewer, info) => {
    const { limit, sortBy } = params
    return await this.postService.getUserPosts(user._id, { limit, sortBy })
  }
})
```

### Method Parameters

1. **Parent Object**: The first parameter is always the parent object (the instance of the type you're adding the field to)
2. **Params**: If you specify `params`, this will be the parameters passed to the field
3. **Viewer**: The viewer object (contains authentication context)
4. **Info**: The GraphQL info object for query optimization

## Model Resolver Options

The `@ModelResolver()` decorator accepts options to customize the field:

```typescript theme={null}
@ModelResolver({
  name: 'allPosts',  // Override the field name (defaults to method name)
  description: 'All posts created by this user',  // Field description in GraphQL schema
})
```

### Available Options

| Option        | Type     | Description                                                 |
| ------------- | -------- | ----------------------------------------------------------- |
| `name`        | `string` | Custom name for the GraphQL field (defaults to method name) |
| `description` | `string` | Description for the field in GraphQL schema documentation   |

## Configuring Return Types

Specify the return type in the `returns` property of `createModelResolver`:

```typescript theme={null}
// Return a single item
@ModelResolver()
latestPost = createModelResolver<UserType>({
  returns: PostSchema,
  resolve: async (user) => {
    return await this.postService.getLatestUserPost(user._id)
  }
})

// Return an array
@ModelResolver()
posts = createModelResolver<UserType>({
  returns: [PostSchema],
  resolve: async (user) => {
    return await this.postService.getUserPosts(user._id)
  }
})

// Return scalar types
@ModelResolver()
postCount = createModelResolver<UserType>({
  returns: Number,
  resolve: async (user) => {
    return await this.postService.countUserPosts(user._id)
  }
})
```

## Field Parameters

You can define parameters for your model resolver fields:

```typescript theme={null}
export const PostFiltersParams = schemaWithName('PostFiltersParams', {
  limit: { type: Number },
  offset: { type: Number },
  sortBy: { type: String }
})

export type PostFiltersParamsType = InferSchemaType<typeof PostFiltersParams>

@ModelResolver()
posts = createModelResolver<UserType>({
  params: PostFiltersParams,
  returns: [PostSchema],
  resolve: async (user, params) => {
    return await this.postService.getUserPosts(
      user._id,
      params.limit || 10,
      params.offset || 0,
      params.sortBy || 'createdAt'
    )
  }
})
```

## Field Middleware

You can apply middleware to model resolvers using `@UseMiddleware()`:

```typescript theme={null}
@ModelResolver()
@UseMiddleware(authMiddleware)
privatePosts = createModelResolver<UserType>({
  returns: [PrivatePostSchema],
  resolve: async (user, params, viewer) => {
    // Only accessible if middleware passes
    return await this.postService.getUserPrivatePosts(user._id)
  }
})
```

## Custom Model Names

By default, the `@ModelResolvers()` decorator uses the class name of the provided schema. You can specify a custom name:

```typescript theme={null}
@ModelResolvers(UserSchema, { modelName: 'CustomUser' })
export default class UserResolvers {
  // Fields will be added to the CustomUser type
}
```

## Handling Relationships

Model resolvers are ideal for handling relationships between your GraphQL types:

### One-to-Many Relationships

```typescript theme={null}
// On the User type
@ModelResolver()
posts = createModelResolver<UserType>({
  returns: [PostSchema],
  resolve: async (user) => {
    return await this.postService.getUserPosts(user._id)
  }
})

// On the Post type
@ModelResolvers(PostSchema)
export default class PostResolvers {
  @Inject(() => UserService)
  private userService: UserService
  
  @ModelResolver()
  author = createModelResolver<PostType>({
    returns: UserSchema,
    resolve: async (post) => {
      return await this.userService.getUser(post.authorId)
    }
  })
}
```

### Many-to-Many Relationships

```typescript theme={null}
@ModelResolvers(UserSchema)
export class UserResolvers {
  @Inject(() => GroupService)
  private groupService: GroupService
  
  @ModelResolver()
  groups = createModelResolver<UserType>({
    returns: [GroupSchema],
    resolve: async (user) => {
      return await this.groupService.getUserGroups(user._id)
    }
  })
}

@ModelResolvers(GroupSchema)
export class GroupResolvers {
  @Inject(() => UserService)
  private userService: UserService
  
  @ModelResolver()
  members = createModelResolver<GroupType>({
    returns: [UserSchema],
    resolve: async (group) => {
      return await this.userService.getGroupMembers(group._id)
    }
  })
}
```

## DataLoader for Performance

For optimal performance, use DataLoader with your model resolvers to batch and cache database queries:

```typescript theme={null}
@ModelResolvers(PostSchema)
export default class PostResolvers {
  @Inject(() => UserService)
  private userService: UserService
  
  @ModelResolver()
  author = createModelResolver<PostType>({
    returns: UserSchema,
    resolve: async (post) => {
      // Using DataLoader, this will batch requests for multiple posts with the same author
      return await this.userService.loadUserById(post.authorId)
    }
  })
}
```

## Best Practices

1. **Use for Computed Fields**: Add fields that aren't directly stored in your database.

2. **Handle Relationships**: Define one-to-many and many-to-many relationships.

3. **Optimize with DataLoader**: Use DataLoader to batch and cache similar requests.

4. **Keep it Simple**: Each resolver should have a single responsibility.

5. **Apply Authorization**: Use middleware to protect access to sensitive fields.

6. **Separate by Type**: Create separate controller classes for each GraphQL type.

7. **Avoid Circular Dependencies**: Be careful with bidirectional relationships to avoid circular dependencies.

8. **Consider Field Complexity**: For computationally expensive fields, consider using query complexity analysis.

9. **Document Your Fields**: Add descriptions to help API consumers understand your schema.

10. **Keep Consistent Naming**: Follow a consistent naming convention for related fields.
