Skip to content

Split code across different files #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
artecoop opened this issue Jun 7, 2022 · 2 comments
Open

Split code across different files #276

artecoop opened this issue Jun 7, 2022 · 2 comments

Comments

@artecoop
Copy link

artecoop commented Jun 7, 2022

Hello Everyone,
I have a pretty large project that I'm trying to move to graphql using fastify, mercurius, mercurius-codegen and mercurius-integration-testing

I've split my business logic into modules, so the code is structured like this

src/
├─ modules/
│  ├─ foo/
│  │  ├─ schema.ts
│  │  ├─ resolvers.ts
│  ├─ bar/
│  │  ├─ schema.ts
│  │  ├─ resolvers.ts
...
├─ base.ts
├─ application.ts
├─ server.ts

base.ts contains the root query and mutation, in order to let other schema.ts files to extend

import { gql } from 'mercurius-codegen';

export default gql`
    type Query
    type Mutation
`;

A schema file contains something like this

import { gql } from 'mercurius-codegen';

export default gql`
    extend type Query {
        list: [String!]! 
    }

    extend type Mutation {
        manage(input: String!)
        delete(id: String!)
    }
`;

Here's a resolver:

import { IResolvers } from 'mercurius';

const resolvers: IResolvers = {
    Query: {
        list: async () => await getAll()
    },
    Mutation: {
        manage: async (_root, { input }) => await manage(input),
        delete: async (_root, { id }) => await deleteString(id)
    }
};

export default resolvers;

In order to avoid bloating the server.ts file, I've created an application.ts to import all the schemas and resolvers

import foo from './modules/foo/schema';
import bar from './modules/bar/schema';

export const typeDefs = [foo, bar] ;

import fooResolver from './modules/foo/resolvers';
import barResolver from './modules/bar/resolvers';

export const resolvers = { ...fooResolvers, ...barResolvers };

The problem arises when I need to set up mercurius:

import { resolvers, typeDefs } from './application';

export const app = fastify();

app.register(mercurius, {
    path: '/',
    schema: typeDefs,
    resolvers: resolvers,
    graphiql: process.env.NODE_ENV !== 'production' && 'graphiql'
});

With resolvers merged with the spread operator, all are overwritten by the last one.

Using mergeResolvers from graphql-tools create a different problem

import { mergeResolvers } from '@graphql-tools/merge';

export const resolvers = mergeResolvers([
    fooResolvers,
    barResolvers
]);

give me this error

Argument of type 'IResolvers<any, MercuriusContext>[]' is not assignable to parameter of type 'IResolvers<any, MercuriusContext, Record<string, any>, any> | Maybe<IResolvers<any, MercuriusContext, Record<string, any>, any>>[] | null | undefined'.
  Type 'IResolvers<any, MercuriusContext>[]' is not assignable to type 'Maybe<IResolvers<any, MercuriusContext, Record<string, any>, any>>[]'.
    Type 'IResolvers<any, MercuriusContext>' is not assignable to type 'Maybe<IResolvers<any, MercuriusContext, Record<string, any>, any>>'.
      'string' index signatures are incompatible.
        Type 'GraphQLScalarType<unknown, unknown> | IEnumResolver | (() => any) | IResolverObject<any, MercuriusContext, any> | IResolverOptions<...> | undefined' is not assignable to type 'IUnionTypeResolver | IScalarTypeResolver | IEnumTypeResolver | IInputObjectTypeResolver | ISchemaLevelResolver<...> | IObjectTypeResolver<...> | IInterfaceTypeResolver<...>'.
          Type 'undefined' is not assignable to type 'IUnionTypeResolver | IScalarTypeResolver | IEnumTypeResolver | IInputObjectTypeResolver | ISchemaLevelResolver<...> | IObjectTypeResolver<...> | IInterfaceTypeResolver<...>'.ts(2345)

Any help?

@mplibunao
Copy link

mplibunao commented Jun 10, 2022

What does fooResolver and barResolver look like?

I'm not sure but my understanding from your example is that it looks like this

import { IResolvers } from 'mercurius';

const resolvers: IResolvers = {
    Query: {
        list: async () => await getAll()
    },
    Mutation: {
        manage: async (_root, { input }) => await manage(input),
        delete: async (_root, { id }) => await deleteString(id)
    }
};

export default resolvers;

Then you're merging them like this

import fooResolver from './modules/foo/resolvers';
import barResolver from './modules/bar/resolvers';

export const resolvers = { ...fooResolvers, ...barResolvers };

which causes Query and Mutation objects to keep getting overridden

You probably want to compose it in a different way like

import { fooQueries, fooMutations } from 'modules/foo'
import { barQueries, barMutation } from 'modules/bar'

const resolvers = {
  Query: {...fooQueries, ...barQueries},
  Mutations: {...fooMutations, ...barMutations}
}

@artecoop
Copy link
Author

I understand your confusion, because of the naming clash, but when I'm importing, resolvers refers to the file name as stated on the structure of the project. I'm not referencing the const resolvers: IResolvers ... (which isn't exported btw).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants