Skip to content
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

Support multiple schemas / endpoint in one project #3357

Closed
michaelloo opened this issue Mar 19, 2024 · 7 comments
Closed

Support multiple schemas / endpoint in one project #3357

michaelloo opened this issue Mar 19, 2024 · 7 comments
Labels
feature New addition or enhancement to existing solutions

Comments

@michaelloo
Copy link

Use case

As a developer working on a large software
I want to be able to use the Apollo SDK with different schemas
So that I am able to request data against multiple GraphQL servers


I am using Apollo on a large codebase with over 50 developers, we split our codebases into multiple different frameworks, so typically we are able to use Apollo SDK (at time of writing using 1.9.0) to generate the models we need to receive or send data to a server. However, some of our frameworks actually interacts with more than 1 server (i.e.: we need to support generating code from more than one schema under the same target).

At time of writing, we have to further split our frameworks in order to avoid filename and namespace conflicts (see thread).

This is not ideal as it requires a large refactoring piece in order for us to keep using Apollo

Describe the solution you'd like

It would be ideal if Apollo could support multiple schemas without the users having to split their codebase into smaller chunks.

Whilst using 1.9.0 the code gen will generate files that cannot be compiled via Xcode as the filename conflicts, e.g.: SchemaConfiguration is common across all schemas and Xcode will complain:

error: Filename "SchemaConfiguration.swift" used twice: 
  'some/path/GeneratedFiles/Schema/SchemaConfiguration.swift' and 
  'some/other/path/GeneratedFiles/Schema/SchemaConfiguration.swift' (in target 'XXX' from project 'XXX')

A couple of suggestions that might work:

  1. Could the schemaNamespace property be used as a prefix to all the generated files?
  2. There are some files that are not added to the enum generated via schemaNamespace like SchemaConfiguration, can they be in the created namespace as well?
@michaelloo michaelloo added the feature New addition or enhancement to existing solutions label Mar 19, 2024
@calvincestari
Copy link
Member

Thanks for the feature request @michaelloo. Apollo iOS is not designed to be used directly with multiple schemas in the same target and I don't believe this is a change we're likely to make in the future. That said, there are a few ways forward:

  1. Apollo Federation - this is the industry standard on how to combine multiple GraphQL APIs into a single federated graph. The benefit with this approach is that your client works with a single GraphQL schema and the router handles the subgraph complexity.
  2. Configure the different schemas into different targets/packages, as we've discussed in the other issue. This unfortunately means managing multiple codegen configurations and doing multiple separate codegen executions. The different targets/packages will get around Xcode limitations with duplicate schema configuration filenames. We also believe this leads to better isolation and separation of concerns.

At time of writing, we have to further split our frameworks in order to avoid filename and namespace conflicts (see #3354 (comment)).

This is not ideal as it requires a large refactoring piece in order for us to keep using Apollo

It's just the generated schema modules that need to be separate, so you may not need to split your frameworks. Your frameworks would then need to gain new dependencies on those separate schema modules, and where you are calling the various APIs currently would need to use the new module namespace.

  1. Could the schemaNamespace property be used as a prefix to all the generated files?

With the above solutions this becomes a project workflow concern; wanting to generate multiple separate GraphQL schemas into a single target. Apollo iOS is not designed for this use case.

  1. There are some files that are not added to the enum generated via schemaNamespace like SchemaConfiguration, can they be in the created namespace as well?

It looks like the SchemaConfiguration enum is the only piece of generated code that is not scoped within the generated namespace when choosing embeddedInTarget as the schema module output type. I'm not 100% sure why not but I'll take investigate further.

@calvincestari calvincestari closed this as not planned Won't fix, can't repro, duplicate, stale Mar 21, 2024
Copy link

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo iOS usage and allow us to serve you better.

@michaelloo
Copy link
Author

Hi @calvincestari, i'll look into the Apollo Federation. In the meantime let me share some more context on our setup.

We have a large codebase with over 50 developers contributing to it. It was divided into modules to make ownership of certain areas clearer. We have local development pods for each of those feature areas. See a simplified example diagram here of what it looked like with 0.52.0:

graph LR
  App --> Module1
  App --> Module2
  Module1_API_Models_For_FooAPI -- generated for --> FooAPI
  Module2_API_Models_For_FooAPI -- generated for --> FooAPI
  Module2_API_Models_For_BarAPI -- generated for --> BarAPI

  subgraph podpsecs
    Module1
    Module2
  end

  subgraph Module1
    Module1_API_Models_For_FooAPI
  end

  subgraph Module2
    Module2_API_Models_For_FooAPI
    Module2_API_Models_For_BarAPI
  end

  subgraph APIs
    FooAPI
    BarAPI
  end

Whilst we we generated the models for the same endpoint in 2 different modules, it was ok because:

  1. They are generated code
  2. The modules are owned by different team who updates them based on their needs (independence)

When we updated to 1.9.0, we've had to adjust some of our modules as they were requesting from different APIs:

graph LR
  App --> Module1
  App --> Module2
  Module1_API_Models_For_FooAPI -- generated for --> FooAPI
  Module2_API_Models_For_FooAPI -- generated for --> FooAPI
  Module2_API_Models_For_BarAPI -- generated for --> BarAPI
  Module2 --> Module2_API_Models_For_BarAPI

  subgraph podpsecs
    Module1
    Module2
    Module2_API_Models_For_BarAPI
  end

  subgraph Module1
    Module1_API_Models_For_FooAPI
  end

  subgraph Module2
    Module2_API_Models_For_FooAPI
  end

  subgraph APIs
    FooAPI
    BarAPI
  end

Whilst this works, it doesnt feel right as we:

  • expose a new podspec for Module2_API_Models_For_BarAPI, so every other podspecs can now also suddenly depend on this module that was only intended to be used by one module
  • we could expose a Models_For_FooAPI since its being used by 2 different modules, but we then have issues with who maintains and owns this module

We've decided to swallow these for now, but it isn't ideal for the way our company operates, as we prefer to have one team owning each modules to reduce the amount of synchronisation needed and communication overhead (especially if you multiply this setup to have about 15 modules)


From a quick read at Apollo Federation, whilst it will indeed support the feature I am requesting here, I am not sure it will support our immediate needs (as it means creating another system in our architecture to host the Router).

We might run into "ownership" of how the generated code is updated, given I assume we'll need to create another module in with the generated code from all the different APIs, which becomes a single point of failure.

We prefer automy and independent delivery of features as much as possible so this might not work for us (unless i misunderstand how this will work on the client side)

@calvincestari
Copy link
Member

  1. There are some files that are not added to the enum generated via schemaNamespace like SchemaConfiguration, can they be in the created namespace as well?

It looks like the SchemaConfiguration enum is the only piece of generated code that is not scoped within the generated namespace when choosing embeddedInTarget as the schema module output type. I'm not 100% sure why not but I'll take investigate further.

Is this the only piece of the puzzle preventing you from using your preferred module configuration in diagram 1?

@michaelloo
Copy link
Author

There is:

  • SchemaConfiguration file and struct name needing a rename / prefix
  • SchemaMetadatafile name needs a rename / prefix
  • Some of the contents of Schema/Objects file name needs a rename / prefix, e.g.: Query.graphql.swift

I feel in general it might be safe to prefix the file name with the schema namespace the same way the structs are part of the schema namespace enum for all generated models, that way we will never get a file name conflict. This will be similar to how some of the protocols in SchemaMetadata are created (e.g.: GQLSchema_SelectionSet). Although this makes it look more like ObjC naming convention now 😞 . But this will address both this feature request and the other bug request mentioned in #3354

@calvincestari
Copy link
Member

I can get behind the argument that SchemaConfiguration should be scoped within the namespace and the fact that it isn't right now with embeddedInTarget could be seen as a bug. In my preliminary testing we can scope it within the enum namespace without any adverse effect to the cache key functionality.

Xcodes inability to handle files of the same name, within the same target even though they're in different paths, is an Xcode bug and best fixed there. In short I'm saying that filename prefixing with the schema namespace is not a change we're willing to make. The codegen templating engine is fairly easy to understand so you should be able to adapt the source to that style of filename output if you really want to go down that path.

@calvincestari
Copy link
Member

@michaelloo - I've created two issues that could alleviate the issues you're experiencing:

We haven't prioritized them into our projected work and they should be relatively easy to get done. If you are interested in collaborating on them we'd be happy to provide design/implementation review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New addition or enhancement to existing solutions
Projects
None yet
Development

No branches or pull requests

2 participants