New domains in GraphQL should be written as GraphQL modules located in projects/graphql-api/src/modules.
When writing a new module, you want to separate out the logic and code of your module, your resolvers and your type definitions.
Import your resolvers and typedefs into the module file in order to keep the code clean and separate. An example module would look like this:
import { GraphQLModule } from '@graphql-modules/core'
import LegacyModule from '../Legacy'
import { yourTypeDefs } from './your-type-defs'
import {
yourQueryResolver,
yourMutation
} from './your-resolvers'
export const YourModule = new GraphQLModule({
context: ctx => ctx,
typeDefs: yourTypeDefs,
resolvers: {
Query: {
yourQueryResolver
},
Mutation: {
yourMutation
}
},
imports: [LegacyModule]
})
Ideally, we want to keep our graphql resolvers to a minimum api logic layer. Business logic should be extracted to separate functions/files to keep the graphql api at a minimum.
const moduleLogger = logger.child({ namespace: 'graphql-api.modules.Banxa' })
export const yourQueryResolver = async (_, { symbols }, { user: { id } }) => ({
try {
const userBalances = await getUserBalances({ userId: id, symbols })
return userBalances
} catch (err) {
moduleLogger.error(err, { fn: 'yourQueryResolver' }, 'Error getting user balances')
throw new Error('Error getting user balances')
}
})
export const yourMutationResolver = async (_, { symbol }, { user: { id } }) => {
try {
const addAssetResponse = await addAsset({ userId: id, symbol })
return addAssetResponse
} catch (error) {
moduleLogger.error(error, { fn: 'yourMutationResolver' }, 'Error adding asset')
throw new Error('Error adding asset')
}
}
Typedefs should be declared and defined in their own file and imported into the module. Keeping the types in their own place helps reduce clutter of the module. A sample type def file should look like this:
import gql from 'graphql-tag'
export const yourTypeDefs = gql`
type userBalances {
amount: String!
}
type addAssetResponse {
assets: [String!]
}
type Query {
yourQueryResolver(symbols: [String!]): userBalances
}
type Mutation {
yourMutationResolver(symbol: String!): addAssetResponse
}
`
You'll want to test your resolvers in a file next to the resolvers with the exension .test.js.
Keeping your resolvers in their own file allows for easy testability.