Skip to content

Conversation

afreeland
Copy link
Contributor

This MR includes an additional example of how it is possible to separate schema and resolvers into separate packages, even external packages, allowing support for larger projects. Hoping this kind of dovetails in the this MR: #3595

The project has the following layout:

tree 
.
├── integration
│   ├── go.mod
│   ├── go.sum
│   ├── integration.go
│   └── schema.graphqls
├── main
│   ├── go.mod
│   ├── go.sum
│   ├── gqlgen.yml
│   ├── graph
│   │   ├── generated.go
│   │   ├── model
│   │   │   └── models_gen.go
│   │   ├── resolver.go
│   │   ├── schema.graphqls
│   │   └── schema.resolvers.go
│   ├── server.go
│   └── tools.go
└── shared
    ├── go.mod
    ├── go.sum
    ├── schema.graphqls
    └── shared.go

6 directories, 18 files

The main package is the centralized graph and will consume the schema files from both the shared and the integration packages.

The main package, after initial generate command, has commented out the resolver: section in the gqlgen.yml file. This prevents the main package from generating all resolvers, which we would like other teams to be able to manage. When generate is ran, it will still generate all model information in the main package, which the external packages can manage.

Additionally, the main package in the resolver.go file tries to lift the generated interface for external resolvers into its own interface called ExternalQueryResolver. This interface is then embedded in in the Resolver like so:

type Resolver struct {
	ExternalQueryResolver
}

This allows the RootResolver to still function as it should but lifts some of the MutationResolver and QueryResolver interfaces that were generated up in a way that other teams can implement the resolver.

Then upon initialization, you can define the ExternalQueryResolver from another package like so:

// Create a new executable schema with the composed resolver
	srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{
		Resolvers: &graph.Resolver{
			ExternalQueryResolver: &integration.Resolver{},
			// Add other team resolvers here
		},
	}))

It was challenging to find a good way to provide a balance of gqlgen built-in functionality in a way that allowed for ease of use, while also allowing still providing some autonomy to other teams.

There are very few discussions on how this is doable and I couldn't really find any examples of doing this in practice. I wanted to put this up in hopes it would help others...or at the very least call out any major flaws that may exist with this.

Describe your PR and link to any relevant issues.

I have:

  • Added tests covering the bug / feature (see testing)
  • Updated any relevant documentation (see docs)

@coveralls
Copy link

coveralls commented Apr 1, 2025

Coverage Status

coverage: 73.374% (+0.03%) from 73.349%
when pulling 02a4d4b on afreeland:large-project-structure
into 492bf92 on 99designs:master.

@@ -0,0 +1,17 @@
module github.com/corelight/integration
Copy link
Collaborator

@StevenACoffman StevenACoffman Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind if we rename this something that matches the directory like:

Suggested change
module github.com/corelight/integration
module github.com/99designs/gqlgen/_examples/large-project-structure/integration

Some of the people who are new to Go have their minds blown when the directory and module names don't match.

@StevenACoffman
Copy link
Collaborator

StevenACoffman commented Apr 2, 2025

Thanks! Also, some larger installations combine other options like use_function_syntax_for_execution_context, omit_root_models and a few other config options to further customize gqlgen behavior and scale even further.

@afreeland
Copy link
Contributor Author

afreeland commented Apr 2, 2025

Thanks! Also, some larger installations combine other options like use_function_syntax_for_execution_context, omit_root_models and a few other config options to further customize gqlgen behavior and scale even further.

Just reading some of the comment descriptions on both use_function_syntax_for_execution_context and omit_root_models, they both seem useful! I guess I kind of skipped both of them somehow when working through things. Appreciate the heads up on those two and I plan on experimenting with those here in just a little bit! 🙏

Hopefully, one of us will get some free time available soon to writeup some more detailed documentation on configs like that and just structuring a project for scale. I definitely think theres a need and the community will benefit from it 😉

Edit: It looks like use_function_syntax_for_execution_context: true is actually missing from the gqlgen.yml that the project gets initialized with. It is in the docs > content > config.md though.

@StevenACoffman
Copy link
Collaborator

I wonder if some members of the community who have experience using gqlgen in larger orgs or teams might offer guidance here?
@a8m @giautm @kanodia-parag @duckbrain

For a single gqlgen project, what gqlgen config works best really changes at a few key points during the course of that project's normal growth and evolution. For instance:

  1. quick proof of concept - single file layout
  2. medium-sized team(s) - follow schema layout
  3. many teams - separate schema and resolvers into separate packages as in this example
  4. very large type systems (more than 65,000 methods) - use_function_syntax_for_execution_context

However, some will instead choose to adopt GraphQL Federation and split into multiple gqlgen instances before one of these growth points is even reached.

@StevenACoffman StevenACoffman merged commit 33f0390 into 99designs:master Apr 4, 2025
17 checks passed
@StevenACoffman
Copy link
Collaborator

StevenACoffman commented Apr 24, 2025

Oh, hey, I forgot to mention another large scale config option is to limit the concurrency for resolvers:

# Optional: Maximum number of goroutines in concurrency to use per child resolvers(default: unlimited)
# worker_limit: 1000

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

Successfully merging this pull request may close these issues.

3 participants