> ## 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.

# Resolvers

> Creating GraphQL API resolvers in Orionjs

Orionjs provides a clean, decorator-based approach to define GraphQL resolvers. The `@Resolvers()` decorator along with `@Query()`, `@Mutation()`, and other decorators make it easy to create type-safe GraphQL APIs with minimal boilerplate.

## Structure and Naming

* Use `@Resolvers()` decorator from `@orion-js/graphql`
* Each resolver class should handle only one operation
* Follow naming conventions:
  * Resolver classes: `{Action}{Entity}Resolvers` (e.g., `GetUserResolvers`, `UpdateProductResolvers`)
  * Mutations: descriptive of the operation (e.g., `listProducts`, `createUser`)
  * Queries: only the name of the entity (e.g., `user`, `productsList`, `blogPosts`)
* Put resolvers in `app/{component}/controllers/resolvers/{Entity}/{Action}/index.ts`

## File Organization

The resolvers folder should have an `index.ts` that exports all resolvers as a single array:

```
controllers/
└── resolvers/
    ├── users/
    │   ├── GetUser/index.ts
    │   └── CreateUser/index.ts
    ├── orders/
    │   ├── GetOrder/index.ts
    │   └── CreateOrder/index.ts
    └── index.ts  # Exports all resolvers as a single array
```

The `controllers/resolvers/index.ts` file:

```typescript theme={null}
import {GetUserResolvers} from './users/GetUser'
import {CreateUserResolvers} from './users/CreateUser'
import {GetOrderResolvers} from './orders/GetOrder'
import {CreateOrderResolvers} from './orders/CreateOrder'

export default [
  GetUserResolvers,
  CreateUserResolvers,
  GetOrderResolvers,
  CreateOrderResolvers
]
```

## Best Practices

* Use `@Inject(() => ServiceName)` for dependency injection of services
* Always define proper schemas for parameters and return values
* Use `createQuery()`, `createMutation()` and `createModelResolver()` functions
* Provide descriptive names and descriptions for GraphQL schema documentation
* Always use `schemaWithName()` for schemas that will be used in GraphQL
* When creating a schema for params, prefer to clone another schema with `cloneSchema()`

## Query Example

```typescript theme={null}
import {Query, Resolvers, createQuery} from '@orion-js/graphql'
import {Inject} from '@orion-js/services'
import {schemaWithName} from '@orion-js/schema'
import {WebsiteConfigsRepo} from 'app/providers/repos/WebsiteConfigs'
import {WebsiteConfigSchema} from 'app/providers/schemas/WebsiteConfig'

const WebsiteConfigParams = schemaWithName('WebsiteConfigParams', {
  websiteId: {type: String}
})

@Resolvers()
export class WebsiteConfigResolvers {
  @Inject(() => WebsiteConfigsRepo)
  private websiteConfigsRepo: WebsiteConfigsRepo

  @Query()
  websiteConfig = createQuery({
    params: WebsiteConfigParams,
    returns: WebsiteConfigSchema,
    resolve: async params => {
      return await this.websiteConfigsRepo.getWebsiteConfigByWebsiteId(params.websiteId)
    }
  })
}
```

## Mutation with cloneSchema

Use `cloneSchema` to create parameter schemas based on existing schemas:

```typescript theme={null}
import {Mutation, Resolvers, createMutation} from '@orion-js/graphql'
import {Inject} from '@orion-js/services'
import {schemaWithName, cloneSchema} from '@orion-js/schema'
import {WebsiteConfigsRepo} from 'app/providers/repos/WebsiteConfigs'
import {WebsiteConfigSchema} from 'app/providers/schemas/WebsiteConfig'

// Define a schema with only the fields we want to update
const UpdatesSchema = cloneSchema({
  name: 'WebsiteConfigUpdates',
  schema: WebsiteConfigSchema,
  pickFields: ['name', 'description'],
})

const UpdateWebsiteConfigParams = schemaWithName('UpdateWebsiteConfigParams', {
  websiteId: {type: String},
  updates: {type: UpdatesSchema}
})

@Resolvers()
export class UpdateWebsiteConfigResolvers {
  @Inject(() => WebsiteConfigsRepo)
  private websiteConfigsRepo: WebsiteConfigsRepo

  @Mutation()
  updateWebsiteConfig = createMutation({
    params: UpdateWebsiteConfigParams,
    returns: WebsiteConfigSchema,
    resolve: async params => {
      return await this.websiteConfigsRepo.updateWebsiteConfig(params.websiteId, params.updates)
    }
  })
}
```

## Simple Mutation with cloneSchema

```typescript theme={null}
import {Mutation, Resolvers, createMutation} from '@orion-js/graphql'
import {Inject} from '@orion-js/services'
import {cloneSchema} from '@orion-js/schema'
import {WebsiteConfigsRepo} from 'app/providers/repos/WebsiteConfigs'
import {WebsiteConfigSchema} from 'app/providers/schemas/WebsiteConfig'

const SetWebsiteConfigNameParams = cloneSchema({
  name: 'SetWebsiteConfigNameParams',
  schema: WebsiteConfigSchema,
  pickFields: ['websiteId', 'name']
})

@Resolvers()
export class SetWebsiteConfigNameResolvers {
  @Inject(() => WebsiteConfigsRepo)
  private websiteConfigsRepo: WebsiteConfigsRepo

  @Mutation()
  setWebsiteConfigName = createMutation({
    params: SetWebsiteConfigNameParams,
    returns: WebsiteConfigSchema,
    resolve: async params => {
      return await this.websiteConfigsRepo.updateWebsiteConfig(params.websiteId, {name: params.name})
    }
  })
}
```

## Mutation Using a Service

```typescript theme={null}
import {Mutation, Resolvers, createMutation} from '@orion-js/graphql'
import {Inject} from '@orion-js/services'
import {schemaWithName} from '@orion-js/schema'
import {SendToInboxService} from 'app/services/SendToInbox'

const SendToInboxParams = schemaWithName('SendToInboxParams', {
  email: {type: String},
  message: {type: String}
})

@Resolvers()
export class SendToInboxResolvers {
  @Inject(() => SendToInboxService)
  private sendToInboxService: SendToInboxService

  @Mutation()
  sendToInbox = createMutation({
    params: SendToInboxParams,
    returns: String,
    resolve: async params => {
      return await this.sendToInboxService.execute(params)
    }
  })
}
```

## Model Resolver

```typescript theme={null}
import {ModelResolver, ModelResolvers} from '@orion-js/graphql'
import {InferSchemaType} from '@orion-js/schema'
import {createModelResolver} from '@orion-js/resolvers'
import {PersonSchema} from 'app/providers/schemas/Person'

type Person = InferSchemaType<typeof PersonSchema>

@ModelResolvers(PersonSchema)
export class PersonResolvers {
  @ModelResolver()
  sayHi = createModelResolver<Person>({
    returns: String,
    resolve: async person => {
      return `My name is ${person.name}`
    }
  })
}
```

## Paginated Query

```typescript theme={null}
import {Query, Resolvers} from '@orion-js/graphql'
import {Inject} from '@orion-js/services'
import {createPaginatedResolver} from '@orion-js/paginated-mongodb'
import {ProductsRepo} from 'app/providers/repos/Products'
import {ProductSchema} from 'app/providers/schemas/Product'
import {ProductsListParams} from 'app/providers/schemas/params/ProductsList'

@Resolvers()
export class ProductsListResolvers {
  @Inject(() => ProductsRepo)
  private productsRepo: ProductsRepo

  @Query()
  productsList = createPaginatedResolver({
    returns: ProductSchema,
    params: ProductsListParams,
    allowedSorts: ['createdAt', 'name'],
    getCursor: async (params) => {
      return {
        cursor: this.productsRepo.loadProductsListCursor(params),
        count: () => this.productsRepo.loadProductsListCount(params)
      }
    }
  })
}
```

## Complex cloneSchema for Params

```typescript theme={null}
import {cloneSchema, InferSchemaType} from '@orion-js/schema'
import {ArticleSchema} from '../Article'

export const ArticlesListParamsSchema = cloneSchema({
  name: 'ArticlesListParams',
  schema: ArticleSchema,
  extendSchema: {
    filter: {
      type: String,
      optional: true,
    },
  },
  pickFields: ['websiteId', 'type'],
})

export type ArticlesListParamsType = InferSchemaType<typeof ArticlesListParamsSchema>
```

## Subscription Resolvers

For real-time functionality:

```typescript theme={null}
import {Subscription, Subscriptions} from '@orion-js/graphql'
import {createSubscription} from '@orion-js/subscriptions'

@Subscriptions()
export class UserSubscriptions {
  @Subscription()
  userCreated = createSubscription({
    params: ParamsSchema,
    returns: UserSchema,
    async canSubscribe(params) {
      return params.name === 'test'
    }
  })
}
```

## Error Handling

Orionjs automatically handles errors in resolvers:

```typescript theme={null}
@Query()
riskyOperation = createQuery({
  returns: String,
  resolve: async () => {
    try {
      const result = await this.someService.performOperation()
      return result
    } catch (error) {
      throw new Error(`Operation failed: ${error.message}`)
    }
  }
})
```

## Context and Viewer

All resolver methods receive the viewer object as the second parameter:

```typescript theme={null}
@Query()
userProfile = createQuery({
  returns: UserProfile,
  resolve: async (_, viewer) => {
    if (!viewer.user) {
      throw new Error('Unauthorized')
    }

    return await this.userService.getProfile(viewer.user._id)
  }
})
```

## Starting GraphQL Server

```typescript theme={null}
import {startGraphQL, startGraphiQL} from '@orion-js/graphql'

startGraphQL({
  path: '/graphql'
})

// Optional: Start GraphiQL (GraphQL IDE)
startGraphiQL({
  path: '/graphiql',
  graphqlUrl: '/graphql'
})
```

## Query vs Mutation

* Use `@Query()` for operations that fetch data without side effects
* Use `@Mutation()` for operations that create, update, or delete data
