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

Namespaces #163

Open
OlegIlyenko opened this issue Apr 10, 2016 · 91 comments
Open

Namespaces #163

OlegIlyenko opened this issue Apr 10, 2016 · 91 comments

Comments

@OlegIlyenko
Copy link
Contributor

@OlegIlyenko OlegIlyenko commented Apr 10, 2016

At the moment all GraphQL types share one global namespace. This is also true for all of the fields in mutation/subscription type. It can be a concern for bigger projects which may contain several loosely-coupled parts in a GraphQL schema.

I tried to describe this feature in detail in following article (which also includes motivation and different use-cases):

GraphQL Namespaces Proposal

After posting this it, I saw interest from different people within the GraphQL community, so I decided go one step further and open this issue in order to suggest inclusion of this feature in the spec and start a discussion around it.

I suggested particular syntax & semantics for the namespaces, but I myself still not 100% sure that it can be used as-is. I hope, that as we discuss it, we would come up with better syntax and semantics (or maybe even entirely different feature) that better fits existing language features and covers described use-cases.

@hepin1989
Copy link

@hepin1989 hepin1989 commented Jun 7, 2016

I think the / syntax is great ,with the current GraphQL spec,we need mount the on GraphQL ObjectType to another bigger GraphQL object as a field and a new name,like Mount Dummy type to a bigger graph as dummyService field.

And still I think #174 have something similar,just a little namespace.

@leebyron
Copy link
Collaborator

@leebyron leebyron commented Jul 2, 2016

Thanks for writing this up, Oleg!

I'm a bit confused as to what this is enabling that isn't already possible or what the immediate goal of using namespaces would be.

I also expressly want to avoid namespaces being included and then people naively namespacing everything just because the feature is there, since that would result in a lot of bloated queries.

For example, in your post you explain that we could write queries like this:

{
  person(id: 12345) {
    id
    firstName

    schema.org/parent {
      givenName
    }

    schema.org/knows {
      givenName
      taxID
    }
  }
}

but I'm curious why you would prefer that over:

{
  person(id: 12345) {
    id
    firstName

    parent {
      givenName
    }

    knows {
      givenName
      taxID
    }
  }
}

The latter seems to represent the same thing, but just requires less typing. Is the goal to just describe what the fields relate to? Perhaps we could have metadata about the fields in the introspection system?

@IamCornholio
Copy link

@IamCornholio IamCornholio commented Jul 2, 2016

I think that the namespaces here enable one to "import" another schema. One could potentially import an schema and build on top of that rather than starting from scratch every time. Implementation wise it looks tough to do though. It might just be easier to import a library (js, ruby, python, java depending on the graphql implementation language) and then build schema using types available from that library.

Another reason why namespaces could be useful is so one can filter a introspection query with a namespace string. In a large system the number of types can make the introspection query results quite unwieldy. But then one could always write a custom introspection query that accepted a filter on a field that's common to all types.

I can think of another use of namespace which is that the number of top level fields (especially those with parameters) and mutations can become quite unwieldy and so one would have trouble viewing them in a graphql api browser like GraphiQL. Having top level fields and mutations further filtered by a namespace would make things easier on the eye.

@OlegIlyenko
Copy link
Contributor Author

@OlegIlyenko OlegIlyenko commented Jul 3, 2016

@leebyron Thanks a lot for looking into this and giving your feedback!

I think we can approach this from 3 different perspectives. First is a semantic meaning of types and fields. This is where schema.org example came from. I think for this one it should be enough to have a user-defined metadata on a field and type level which is exposed via introspection. I think community then can come up with conventions for how to encode this semantic information for use in different tools.

The second is the fact that the mutation (as well as subscription) fields share global namespace. Mutation type is the only place where mutation can be defined for a whole GraphQL schema. This is where my original motivation came from. I went into details about this particular point in the original post ("Mutation Type" section).

There are several factors to why I see it as a concern. At the moment mutation type should be an ObjectType, which means there that all mutation fields should be defined in it. This makes it hard to work with loosely-coupled parts of the schema which potentially may have name conflicts. In my opinion, one of the contributing factors is also the lack of polymorphism in input objects. This greatly limits amount of logic one can encode in one particular mutation field, which means that different variations of updates need to be represented as top-level mutation fields.

Namespaces can provide a solution for this particular problem, but I'm pretty sure that there are also other approaches we can take. For instance, let's imagine that it is possible to use a union type as a mutation type:

type AuthorMutation {
  create(firstName: String!, lastName: String!): Author
  changeName(id: String!, firstName: String!): Author
  delete(id: String!): Author
}

type ArticleMutation {
  create(title: String!, authorId: String!): Article
  changeText(id: String!, text: String): Article
  delete(id: String!): Article
}

union Mutation = AuthorMutation | ArticleMutation

schema {
  mutation: Mutation
}

a query like this would not make much sense because field name is ambiguous:

mutation {
  create(firstName: "John", lastName: "Doe") { id }
  delete(id: "1234") { id }
}

but we can use an inline fragment to disambiguate it:

mutation {
  ... on AuthorMutation { 
    create(firstName: "John", lastName: "Doe") { id }
  }

  ... on ArticleMutation { 
    delete(id: "1234") { id }
  }
}

If we provide a shorthand syntax for inline fragments, it can become a bit more cleaner:

mutation {
  AuthorMutation/create(firstName: "John", lastName: "Doe") { id }
  ArticleMutation/delete(id: "1234") { id }
}

This code violates the semantics of inline fragments and union types - they normally represent 2 mutually exclusive fields, still I use them here just to disambiguate the field names, so both mutation fields would be resolved. For the sake of example I use this approach simply because there is already syntax available for it in the language. The point I would like to make is that this example does not involve namespaces, still it addresses my issue with the global namespace for the mutation fields.

The third perspective is the the global namespace for the types. I recently had a few conversations with different people who are thinking on introducing GraphQL in their projects and companies (from small to pretty big). One question that came up several times in the discussion is whether GraphQL should serve as a facade for different internal services managed by different teams and if yes, then how does DDD fit into this picture. I do believe that GraphQL is a perfect fit for a facade API. Many client applications often have cross-cutting concerns, so it is very helpful to provide a facade for these client through a single GraphQL API, especially if clients are constrained in some ways (like mobile clients or embedded devices). On the other hand I find it helpful to think about different internal services as a bounded contexts. Each bounded context comes with it's own ubiquitous language. Given a system designed with these principles in mind, name conflicts are actually desired, but ubiquitous language within different bounded contexts can define very different semantics for the same terms (like User, Account, Product, etc.). I don't want to say that it's the best way to model system or anything like this, it's just some people use it and find it helpful for their own systems (including myself). This is where namespaces can be a very helpful tool when designing a GraphQL facade that aggregates several bounded contexts. I would be really interested to know your opinion on this topic. Do you think that GraphQL is suitable as facade in these kind of systems and if yes, then will you try to define the single model for all bounded contexts or you will try to preserve the ubiquitous language of every bounded context and minimize a common/shared area of the schema? (/cc @dschafer, @schrockn)

I also expressly want to avoid namespaces being included and then people naively namespacing everything just because the feature is there, since that would result in a lot of bloated queries.

Indeed, I do share this concern. I guess if one asks himself whether he/she needs to use namespaces, then the answer is probably no. On the other hand, as I described above, namespaces may be a useful tool to solve particular challenges (although, not the only possible tool in every given case).

The latter seems to represent the same thing, but just requires less typing. Is the goal to just describe what the fields relate to? Perhaps we could have metadata about the fields in the introspection system?

Indeed, in this particular example query I would normally skip the namespaces. This is the nice thing about the namespaces in contrast to naming conversions and other namespace emulation techniques which have quite a few disadvantages: even in presence of namespaces one can just skip them if they are not necessary (some implicit namespacing rules will apply in this case). There are also a few reasons why I may use explicit namespace in this query:

  • As you mentiond, I may use it to make it clear, for people who view this query, that this field is defined in schema.org spec and has particular semantics associated with it (best type of documentation:))
  • I anticipate change to this type in future and would like to keep my query forward-compatible
  • After some time we decided to implement schema.org interface in this type, but this cased name collisions. I will then use namespace to disambiguate the field. This is especially important in case of mutations (as i described above), where name collisions are intentional.

That said, I don't really want push concept of namespaces per se. My main goal is to address some of the issues, like global namespace for a mutations fields. Whether it is solved with namespaces or not does not really matter. I hope what I described here reveals a bit my original motivations for this proposal :)

@aschrijver
Copy link

@aschrijver aschrijver commented Jul 31, 2016

Hi @OlegIlyenko ,

Nice elaboration and spec you`ve written down!

In general I see a clear need for a namespaces mechanism. I am currently writing microservices that communicate using graphql. With microservices architecture each microservice represents a separate bounded context (in DDD terminology) and you can easily get type conflicts when there are many of them around. I.e. same name, different semantics, etc. Also - like you said - there will be a need for common (base) types to be defined.

Your proposal certainly has its charms, especially the notation. Yet I also have a number of reservations:

  • Additional complexity in server implementation while namespaces support could be optional
  • Less visual appealing, harder to grasp query language (visual clutter, readability)
  • It is opinionated. It offers a single way to define namespaces, while there are many existing ones around
  • You will either stay opinionated and have people stick other namespace implementations on top of their implementation, or you will have to gradually adopt all the nooks and crannies that are also in other namespaces to take care of special cases ('my namespace string has a slash', 'mine is an object, not a string', etc.).
  • It is a breaking change! Or at least it will be in environments where the server cannot dictate the query format and there are multiple spec flavours around (old and new ones)

In another issue I proposed a more general metadata mechanism, that is an extension and not breaking. See #200
The namespace could be defined in this metadata using any standard mechanism that is around (well, there should be some compatibility with JSON actually, but still).
The structure of the metadata is defined like any type and there could be introspection support with a __METADATA type.

Using this metadata proposal for namespaces, however, is far less powerful than what you propose. But it can be adjusted. What I like in this is the freedom it leaves to implementers.
Having metadata support we could wait and see how the community use it, what metadata formats are popular, before adopting a specific approach (Hell, there could be a Metadata Profiles feature in future and a 'marketplace' for profiles and other extensions. Yeah more marketplaces..)

Finally, I would be in favour of having a lean and mean base spec, and then have an extension mechanism that allows you and others to offer the kind of cool stuff you propose. It could be a standard extension. And I could consult a server to see if it supports this feature before sending my query (Note this is similar to the extension mechanism in OASIS CMIS spec and others).

Arnold Schrijver

@yarax
Copy link

@yarax yarax commented Aug 16, 2016

Just a note: I'm currently writing a wrapper around existing REST approach based on microservices (with plenty of endpoints) and namespaces are very wanted, otherwise all type names look like fact_reports_pp_campaigns_delivered

@domkm
Copy link

@domkm domkm commented Aug 23, 2016

I would also appreciate namespaces for all of the reasons stated above by @OlegIlyenko.

@leebyron Given that Facebook has nearly 10,000 types, how does it keep track of type names and query/mutation fields (which I am assuming there are hundreds if not thousands of as well)? Are there any best practices for merging schemas from multiple sources?

@xuorig
Copy link

@xuorig xuorig commented Nov 23, 2016

As our mutation root grows we've been asking the same questions internally.

Given that Facebook has nearly 10,000 types, how does it keep track of type names and query/mutation fields (which I am assuming there are hundreds if not thousands of as well)?

Anyway you can share alternative solutions or insight on how you've been handling that problem @leebyron 🙏 ?

@smolinari
Copy link

@smolinari smolinari commented Nov 24, 2016

This might be totally off the wall and possibly even a stupid comment showing my lack of understanding, but couldn't different URLs and a routing middleware be used like namespaces? One could split up a larger app among different endpoints that way.

So, a bigger site would have

https://some-site.com/user
https://some-site.com/blog
https://some-site.com/admin

etc.

And if things are getting too involved even with this "namespacing", you could also break it down more.

https://some-site.com/user/profile (or register)
https://some-site.com/blog/page (or /gallerie)
https://some-site.com/admin/blog

I know, I know. We are heading towards something RESTy-like again with that idea, but it isn't really REST, right?

This might not solve the microservice communication issue, which I believe this idea came from. But, I think it solves the "big schema" and "name collisions" issues, since each endpoint would have its own schema. You'd also get the decoupling, which was asked for. It would allow for better asynchronicity too, because you could hit different endpoints at the same time, if needed, which I don't think would be too often. It would allow for parallelism, because breaking up into separate endpoints, theoretically, would allow a system to be scaled out much more easily later on too. And, if you refine the URLs, it isn't a breaking change.

The cons would be duplication. But really, if an app gets that big, I would imagine creating schema would be automated in some way anyway, right? Or does Facebook really have their devs go into the schema and alter or add to it all the time? I find that hard to believe.

I'm sure this blows away some other general concept that makes GraphQL so great, which I am completely missing due to my own lack of understanding. So, please do give me hell. All I can do is learn. 😄

Scott

@khamusa
Copy link

@khamusa khamusa commented Jan 9, 2017

I'd like to note that allowing namespaces to be optional as long as they're unambiguous may difficult future developments of the schema, since you cannot safely add new types that would add namespace ambiguity having already allowed clients to write unnamespaced queries.

Regarding syntax, / seems an interesting choice, but I'd also propose . as a separator (in my opinion it is slightly less cluttering).

@IvanGoncharov
Copy link
Member

@IvanGoncharov IvanGoncharov commented Jan 9, 2017

As @khamusa point out:

you cannot safely add new types that would add namespace ambiguity having already allowed clients to write unnamespaced queries.

So you force all consumers of your API to bloat their queries by prefixing every type and field.
I really like the idea that GraphQL force you to define a consistent vocabulary for your system.

And the biggest concern I have is that people starting to auto-generate one huge GraphQL proxy to expose a bunch of microservices without even try to reuse common types. And instead of solving communication problem in your organization you forcing your API clients to work around this by using prefixes and reading a ton of docs to understand the difference between: schema.org/Person, Accounting/Person, CRM/Person, etc.
Moreover, I imagine a lot of developers will abuse this to implement versioning and I don't want to constantly use v1, v2, v3 namespaces.

It certainly possible to do the same stuff right now by making v1_Accounting_Person but it looks ugly and not encourage by any existing GraphQL feature.

Finally, as it was pointed out previously Facebook has nearly 10,000 types without any name clash.
And I don't think many other API will ever approach this number.

@leebyron
Copy link
Collaborator

@leebyron leebyron commented Jan 9, 2017

@IvanGoncharov illustrated a number of API design abuse patterns that worry me as well.

I've still yet to hear a compelling argument for why namespaces are necessary. The only argument I've heard so far is naming collision. Frankly, that's been a non-issue for us at Facebook and our schema continues to grow well over 10,000 types.

We avoid naming collisions in two ways:

  1. integration tests.

We don't allow any commit to merge into our repository that would result in a broken GraphQL schema. This test is pretty simple and fast to run, we just boot the server and if it fails to boot with a GraphQL error, we fail the test. Two types or fields on a type of the same name would cause such a failure. This kind of test ensures our master branch isn't constantly broken, and protects against name collision problems. If someone accidentally choose the same name for a new type, they can resolve the problem. Often, resolving it actually means using the existing type rather than renaming the new one. That keeps our API internally consistent and avoids duplicate information.

  1. Common naming patterns.

We have common patterns for naming things which naturally avoid collision problems. For example, mutations often follow a "verbNoun" pattern such as "likeStory" assuming that the noun is the type it operates on and your types also don't have conflicts as well, this naturally avoids conflict. I suppose with namespaces you could have written "Story/like" but that doesn't offer clients anything they didn't already have, and it might force them to handle multi-part names when reading the result. For types, we try to generalize as much as possible so that we have types shared across products. That makes future product integrations easier as well. For products that shouldn't integrate, or have similar concepts that are rightfully different, like a Facebook Event and an advertising logging Event, we alter the name to clear the ambiguity for the reader.

@mvpmvh
Copy link

@mvpmvh mvpmvh commented Jan 16, 2017

Say I'm looking at your API in graphiql, I'm either a new fb hire or some dev looking to integrate with a part of your API. I pick 1 of your 10,000 types and I want to dig into the mutations that fall under this type. How do I, as a new hire or third-party dev, easily consume this information?

@maciejkrol
Copy link

@maciejkrol maciejkrol commented Feb 21, 2017

For products that shouldn't integrate, or have similar concepts that are rightfully different, like a Facebook Event and an advertising logging Event, we alter the name to clear the ambiguity for the reader.

@leebyron How do you not end up with horrible caterpillar names like @xuorig said? I have not so very big schema and this is already a problem.

I tried to look for examples of big schemas or best practices. Nothing. Every article I could find is based on a schema with ~3 simple types. If you have anything please consider answering this question.

@leebyron
Copy link
Collaborator

@leebyron leebyron commented Feb 21, 2017

Say I'm looking at your API in graphiql, I'm either a new fb hire or some dev looking to integrate with a part of your API. I pick 1 of your 10,000 types and I want to dig into the mutations that fall under this type. How do I, as a new hire or third-party dev, easily consume this information?

Typically our types do not correspond 1:1 with mutations. We typically do not have CRUD style mutations, instead we often have RPC style mutations that correspond to user events. In the cases that there is a closer relationship, the most common mutations are typically mentioned in the description of the type. For example the Story type includes a mention in it's description of the likeStory mutation, since it's a common question.

As I mentioned in the prior comment, naming conventions can help a lot. If you're looking at GraphiQL, the ideal experience is that you start typing what you expect to work and get typeaheads with helpful suggestions. In addition to this, both search in the documentation explorer, and helpful descriptions with recommendations for common mutations are all part of the strategy of providing information in the right places at the right time.

@leebyron
Copy link
Collaborator

@leebyron leebyron commented Feb 21, 2017

@maciejkrol - our type system has many types, but with some clear and easy to follow naming conventions we balance between clarity, conciseness, and expandability.

The primary goal when naming things in a type system expected to expand in the future is to have clear answers for these two questions:

  1. Does this name introduce ambiguity? (e.g. if a type is called X, do you find yourself asking "what kind of X is this?")
  2. Can we imagine future expansions of this API in which this name will become too generic and therefore ambiguous? This question is much more difficult to answer, but forward-thought is always helpful when designing long-lived systems.

For example at Facebook we have a type called User, which in the beginning was clear via context that this meant a User of facebook.com. As our schema expanded, we introduced other types like InstagramUser, ThirdPartyUser, and other more specific types unique to certain circumstances or areas of the schema. Interface and Union types then help use related types together. For example, we have an interface called Actor which represents the entity which performed some action in the product, which is often a User but could be a handful of other types. We also have some type names that are longer and much more specific like EventsCalendarDateTime which belong to specific sub-products and have product-centric nuance that would be inappropriate for more generic shared types. In general this balance has worked for us: terse names for broadly used types and specific unambiguous names for specific or single-purpose types.

@Cyberlane
Copy link

@Cyberlane Cyberlane commented Mar 9, 2017

@leebyron my only concern with your suggestions is that the Docs link within the GraphiQL will show a massive list of mutations under Mutation. Is there perhaps a way to decorate each item with some type of Category which will only affect the generated docs, whilst leaving the actual schema alone?

Our schema has two entry points, which is really nice.

Our mutations however are in the hundreds... and without being able to split them up a bit, the generated documentation becomes useless to our developer teams.

@syrusakbary
Copy link

@syrusakbary syrusakbary commented Mar 9, 2017

@Cyberlane this issue might be related #252 :)

@jhclouse
Copy link

@jhclouse jhclouse commented Jun 14, 2017

The ideal situation is one in which you have complete control over the design of your schema and can justify a well-crafted set of type names. Unfortunately, many of us are in a different situation: we have an ever-growing and ever-changing set of disparate backend data sources described with something like Swagger and would love to be able to import them on the fly without curating them. Maybe namespaces aren't the best idea. But it would be great to have some kind of support for this scenario.

@isaksky
Copy link

@isaksky isaksky commented Jun 30, 2017

@leebyron For cases where you know the object you would like to mutate, but don't know what cutesy name your colleagues decided to give the mutation (updateStory, changeStory, likeStory), I think this would get annoying. Think about this: why do we organize our functions into modules and namespaces as programmers? Don't those reasons also apply here?

Also, what about allowing it only for top level fields? A lot of what I see in this thread looks messy/overkill, but allowing it just for the top level would solve most of the problem. Fields after the first level already have a context, so further namespacing after that may cause more problems than it solves.

@KieronWiltshire
Copy link

@KieronWiltshire KieronWiltshire commented Jul 19, 2017

https://github.com/Quture/app/issues/1

This is exactly what I need for ^^

@RickMoynihan
Copy link

@RickMoynihan RickMoynihan commented Sep 11, 2017

Hi,

I'm pretty new to GraphQL, so please take my comments with a pinch of salt, but I see the need for namespaces for some usecases, and I think there are several distinct reasons to want them.

  1. If you're generating your schemas dynamically for example from a database schema then the possibility of collisions is undesirable, and having to prefix fieldnames/typenames/enums etc is pretty ugly.
  2. More importantly without namespaces it's hard to see how it's possible to share schemas and target them to create portable clients across different services. Without them how do I know that the field droids contains the rebel droids I'm looking for? Namespaces would help turn schemas into contracts and enable extensibility and discovery.

You might say why do we need portable clients? But we already have the need for one as the graphiql IDE is used across many implementations and it appears to rely on __schema being a contract with the server. The use of the __ prefix here seems to be a tacit acknowledgement of the role of namespaces as __schema really means graphql/schema (to borrow the syntax of this issue). The problem is that __schema is effectively now reserved, embracing namespaces would be embracing 3rd party extensibility and would turn a closed system into an open one.

Namespace & module systems often allow importing a namespace as an alias, e.g. you could imagine something like the following (inspired by SPARQL), though I suspect you could find a prettier syntax:

ALIAS myns: <http://foo.com/mynamespace> 
ALIAS schema: <http://schema.org/>

{
  schema/name 
  myns/name
}

Unnamespaced names with the exception of __schema could then default to being in an anonymous namespace for backwards compatibility.

@leebyron's idea of putting this data into the introspection system sounds like an interesting proposal, though I'm not yet sure if it would be equivalent to first class namespace support.

@thecaddy
Copy link

@thecaddy thecaddy commented Sep 29, 2017

This should happen. Lee may not support it but the standard is bigger than FB now.

👍

@alloy
Copy link

@alloy alloy commented Nov 23, 2017

I don’t want to really double post and definitely don’t think my thoughts on the topic are crystallised well enough to actually partake in this debate, so for now I wanted to just link to some rough thoughts on this subject in the context of ‘stitching’ schemas from various services ardatan/graphql-tools#495 (comment)

@zcourts
Copy link

@zcourts zcourts commented Mar 26, 2019

@leebyron so we're wrapping up work around this in our next sprint.

We have a customer who is going to start using it.

What do we want out of this? What does success or failure look like?

It's unclear to me what would be useful and what would be sufficient evidence of this feature's usefulness or lack thereof for this ticket to be moved forward towards being included in the spec or otherwise officially adopted.

@leebyron
Copy link
Collaborator

@leebyron leebyron commented Mar 26, 2019

I'm glad to hear you're trying this idea out in practice and collecting real world information on it's usage. This is the best way to drive these kinds of discussions to a conclusion.

Considering the debate and strong concerns voiced on this topic over time I think a few things would be important for moving forward:

  • A clear report of exactly what was built. Namespaces has meant many things to many people, different suggested approaches have had different tradeoffs, and knowing the exact set of expanded syntax and behaviors is helpful in pinning down a specific proposal to aid discussion.

  • A sense of how many / what proportion of your customers use this feature and how pervasively it's used. GraphQL has historically done a good job of remaining simple by avoiding feature creep for rarely used features.

  • Highlighting concrete value added and new use cases that were previously not possible without namespaces. This will help answer the question "Is it worth its complexity cost"

  • Ground theoretical concerns in reality. An objective report of real world usage shouldn't just attempt to address concerns but instead determine which theoretical concerns did not manifest, and which were real and how they were addressed in practice.

Above all I think it's useful to consider the Guiding Principles

@morris
Copy link

@morris morris commented Jul 12, 2019

I worked through this as my client is looking for a way to organize an (allegedly) large number of types and mutations and types. I'll add a few observations and ideas which I believe haven't been mentioned so far.

Concerns about documentation (e.g. too many mutations in GraphiQL) can and should easily be solved by using directives and enhanced tooling. For example, a @doc(tags: ["customer", "order"]) directive could be consumed by GraphiQL (or alternatives) in order to render type and mutation docs structured by tags.

Also, adding namespaces just for the sake of reducing name collisions is equivalent to prefixing, on all levels except syntax, because

  • namespaces would have to be introduced in an optional fashion to be backwards compatible,
  • namespaces could not be omitted in any query or definition because they could become ambiguous in the future,
  • and namespaces are subject to name collisions themselves.

I don't see any circumstance where you could omit a namespace, neither as an author of a GraphQL API, nor as a consumer. You do save a few prefixes in namespace foo { type Bar { ... } type Foo { ... } } compared to type FooBar { ... } type FooBaz { ... } but that certainly does not add enough value.

This extends to federation/stitching scenarios. If namespaces of the same name are combined additively, the name collision problem is not solved, just moved. If your federation/stitching scenario must handle name collisions (which is questionable design, in my opinion) you really only need a flexible combination mechanism (e.g. something like import Bar as FooBar from "foo.gql").

As a side-note, TypeScript's namespaces have very little usage because the ES6 import syntax is both simple and powerful. This could be an indication that an improved import mechanism could be a solution to saving one from writing prefixes (although, again, this could be solved outside the spec with tooling).

@emceelovin
Copy link

@emceelovin emceelovin commented Mar 25, 2020

Hey, everyone! I was just thinking about how namespaces would be "useful," but it was more for organizing queries and mutations that were specific to that namespace type. What I mean is, when looking at a schema through the documentation on say, playground, everything is in the "global" namespace. I think it would be nice to organize things into namespaces. @OlegIlyenko , as @leebyron said, you can still offer "namespaces" with the current spec. how i, and you, could do it, is as follows:

type QueryNamespace# Namespace {
field | field(...) : # Field that would otherwise be on type Query or type Mutation
}

type Query {
category: QueryNamespace!
}

query NamespaceQuery {
category {
field | field(...){
}
}
}

What really got me curious about namespaces, was the elimination of the field/type prefix. Instead of having a bunch of queries and mutations, potentially polluting the "global namespace," would namespaces be beneficial, or negative? I am asking this from the pro's POV. This would yield the following organization:

type Query {
namespace chat {
groupList: GroupListResult!
p2pList: P2PListResult!
}
}

query {
chat{
groupList{
...
}

    p2pList{
        ...
    }
}

}

versus:

type Query {
chatGroupList: GroupListResult!
chatP2PList: P2PListResult!
}
}

query {
chatGroupList{
...
}
chatP2PList{
...
}
}
}

@david-wb
Copy link

@david-wb david-wb commented May 10, 2020

I think namespaces could be very useful in a microservice architecture. Would a syntax like below be a good idea?

namespace UserService {
  type User {
    ...
  }

  type Project {
    ...
  }

  type ProjectMember {
     ...
  }

  type Query {
    currentUser: User!
    projects: [Project]!
    ...
  }

  type Mutation {
    createProject(name: String!): Project!
    deleteProject(projectId: String!): Project!
    ...
  }
}

Then on the client side we would write queries like this

query {
  namespace UserService {
    currentUser {
      name,
      email
    }
  }
}

My current strategy is to prefix all types, mutations, and query names with the service name like UserService_ but it seems unfortunate to have to resort to that.

@david-wb
Copy link

@david-wb david-wb commented May 10, 2020

Namespaces wouldn't change the fact that the graph is meant to articulate entities. They would merely be a GraphQL language feature that allows name reuse across schemas.

Partial resolution wouldn't be a problem because a service could still use and extend the namespace of another service. Just my two cents. I will leave it at that.

@zcourts
Copy link

@zcourts zcourts commented May 15, 2020

@leebyron - reporting back on this since it seems to be kicking up again. We did get this to production but it's not a huge/main roadmap item so has just been on the sideline.

What was built

We implemented namespaces as follows.

SDL changes

  1. Declare a namespace using namespace <name>
  2. All declarations in SDL following a namespace declaration are a part of that namespace.
  3. For compatibility with existing schemas, if no namespace is present in an SDL file OR any declarations precede a namespace declaration then those declarations are put into a namespace called default
  4. If the same namespace is encountered again later, it acts as a switch and subsequent types go into the existing namespace of the same name.
  5. Query, Mutation and Subscription (or whatever you call these in your schema declaration) are treated the same as other types in terms of which namespace the belong to.
  6. If an input, type or interface is defined multiple times in the same namespace then they are merged by doing a union of the fields in all declarations.
  7. Two declarations of the same name can have the same field, if they do then the last one declared replaces the previous declaration.
    We use this to enable overloading functions/fields especially in Query, Mutation or Subscription.

Imports

Imports enable making a symbol from another namespace available in the current scope without fully qualifying it.
This is supported in SDL and queries.
In SDL:

  1. import b.T - makes T from namespace b available in the current namespace without prefix
  2. import b.T as T1 - aliases b.T so that it can be accessed in the current namespace without a prefix as T1
  3. import b._ - imports all symbols in the namespace b making them usable in the current namespace without a prefix.
    If b includes names that collides with what's in the current namespace then an error is raised at the import line informing of conflicting names (with a list of conflicting names)

Parameters

  1. Parameters can optionally have a namespace prefix e.g. findX(filter: b.FilterX)
  2. If no namespace is used then assume the default namespace.

Querying

Assume two namespaces a and b and query findX: Int is defined in b.Query.

  1. Running a query optionally requires a namespace prefix.
  2. You can run with {findX}, {b.findX}
  3. Resolving findX uses the simple lookup.
    Presume all available functions are in a map functions then finding the function to execute is fn = field.contains('.')? functions[field] : functions['default.' + field].
    Then execute fn as would've been done prior to namespaces. In other words, if the field being resolved contains . it's already namespaced, execute it, otherwise prefix the field with the default namespace.
    This is of course only an example, in our case we needn't prefix anything because findX is in the map directly so functions[field] works whether namespaced or not.

Imports

A query can be preceded by a list of import statements as defined in the Imports section above.
If a type from another namespace is imported then it needn't be prefixed.

Value added

Background, Hypi is a dev. platform that abstracts traditional software dev. lifecycle into simple, programmable steps providing a micro-service like app dev. workflow without the complexities or boilerplate.
With a few clicks our customers can have multiple scalable, secure backends ready to go.

You create "apps", each app has its own GraphQL schema, releases etc. From the schema the platform generates input types and a CRUD API, you can attach directives to fields to call serverless functions or HTTP APIs.
The big one for us is, each app can have a "hard" dependency on another app.
By hard, I mean, in the same way npm, maven or other dependency management works - by adding a dependency the platform will do what others call "schema stitching".
The number of types in a schema grows very quickly. Especially because we allow two customers to give permission to add each other's apps as dependencies.
This will get worse when we launch our dev. market place allowing developers to build and publish apps that others can use.

  1. For us pushing all types into one schema has been a nightmare for some customers.
    They can have multiple teams working on different products but share features. It forces co-ordination that some haven't found easy to do.
  2. We attempted the "prefix each type with something" but ended up with types/APIs that were sometimes just unreadable.
    It makes development a bit frustrating when one function name is almost half of a line.
    This was the case because the only sure way we have of doing unique names is to combine customer realm name, app name, release name, type and field.
    I read the arguments above about this point being a matter of convenience but I don't think code that looks like it was generated and scrambled is a matter of convenience to be rid of.
    So this directly translates to better productivity for our customers.

Our usage may seem niche but I think in larger companies this would also occur.
3. "hiding" imported types is made possible. Depending on app A doesn't mean I want all of A's types or APIs to be presented with mine.
We have not implemented this directly since we already have something in place.

Problems & solutions

  1. The biggest issue we had wasn't in implementation but was in using it...obviously no GraphQL client supports it currently so the client echo-system was almost completely lost.
  2. Some effort was made to look at adopting Apollo to support the syntax in queries but wasn't completed.

Proportion of customers using

We handpicked 10. Of those chosen, all adopted it. The one prevailing pain point was the lack of client support.
It was preferred over the old schema merging approach.
It is disabled by default but is in production. These 10 have it enabled and continue to use it.
We won't allow it to be enabled until there's some direction in the community or we have the bandwidth to add support to at least the top 3 GraphQL clients in use by our customers.

GraphQL guiding principles vs our product

Our approach is in direct contrast to some of the GraphQL principles. In particular, we declare that our aim is to "simplify software development".
One of the things that statements translates to for us is convenience.

Other notes

  1. It was relatively easy to modify graphql-java to support this server side.
  2. There was no impact on query performance
  3. There was no impact on SDL parsing performance

I think this covers everything. I had only a limited involvement in the actual implementation so this is from the design discussions prior to impl. + what I know from using our APIs.

@zcourts
Copy link

@zcourts zcourts commented Jun 6, 2020

How do we make progress on this? The ticket is marked as having no champion, this is something we're quite keen to push so I'd be happy to take this on if I had clear steps to taking this from idea to being drafted in the spec.

Is it reasonable to propose an RFC based as the above next step?
Keen to move forward

@benjie
Copy link
Member

@benjie benjie commented Jun 6, 2020

@zcourts I suggest you add it to the next Spec WG for discussion; allocate about 20 minutes to it I think.

https://github.com/graphql/graphql-wg/blob/master/agendas/2020-06-04.md (Ignore the filename, it's actually the 11th.)

@zcourts
Copy link

@zcourts zcourts commented Jun 6, 2020

@benjie
Copy link
Member

@benjie benjie commented Jun 6, 2020

Excellent; make sure you have a (re-)read of https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md before you come, and have a compelling argument for the new use cases that this change enables (i.e. an answer to "Favor no change" in the guiding principles).

In particular, it would be good to have an answer to how prefixing things with a namespace is a significant improvement over prefixing them with text; e.g. findX(filter: b.FilterX) vs findX(filter: b_FilterX). I read your argument about "the only sure way we have of doing unique names is to combine customer realm name, app name, release name, type and field" but don't follow why whatever you use for the namespace name couldn't be used as the textual prefix?

I also wonder if the solution to this might be better implemented as build-time schema tooling rather than expression through the final GraphQL API.

@ieugen
Copy link

@ieugen ieugen commented Jun 26, 2020

I've read the meeting notes. Waiting for this feature :) .

@b-jsshapiro
Copy link

@b-jsshapiro b-jsshapiro commented Jan 18, 2021

I agree that GraphQL needs namespaces - or rather, that it needs a module system in the programming language sense. I think Oleg's proposal is interesting, but I think it's not the best way to proceed. It adds a lot of baggage that will make future evolution difficult. I feel that @zcourts is on the right track, but I think his proposal could be improved in several ways. Though it deserves a proper answer, I think the response to @benjie about "why not text mangling" is pretty simple to address.

Suggested changes to @zcourts proposal

  1. Namespaces should be delineated by brackets {}. Experience in other languages that have approached this without bracketing has been poor, with the result that all of those languages have moved to add delimiters to namespaces. It's critical because it permits namespaces to be nested. Also, it follows the lexical structure of types (see below).

    Please, let's not force ourselves to re-learn the lessons of the past around this issue. This is actually one of the bigger mistakes in the Java package declaration mechanism and also the early C++ namespace declaration mechanism.

  2. The import syntax should follow existing well-worked examples for the "what is being bound" part of the syntax. I recommend adopting the surface syntax used by Typescript, which supports selective import, renaming at import, and whole namespace import. But whatever is adopted, this is a place in a language design that is easy to get wrong. Invention should be avoided if possible, and there is no real need for invention here.

    The change relative to Typescript is that the thing on the right of the import (that is: the thing we are importing from) is a fully qualified type name resolved against the top-level scope. Namespaces are merely types in disguise, see Whole Namespace Import below. The selectors in the binding portion reference names within that type name.

    Note that if we choose to admit nested type definitions _or_nested namespaces (which I believe we should) then there is no semantic difference between:

    import { x.y as localName } from ThatNamespace
    

    and

    import { y as localName } from ThatNamespace.x
    
  3. There should be a corresponding syntax for re-export. Here again, the Typescript surface syntax is a good model to steal. Because GraphQL uses LETREC (mutually recursive) definition bindings, there's no real problem created by re-exporting types from a namespace. Here again, I'd borrow the syntax from typescript. The ability to re-export names gives us something very similar in flavor to NPM packages, where the intended public interface can be exported selectively from a library.

    If it weren't for enabling the package publication idea, I would say this could be deferred. Given how small a delta it is and how slowly the GraphQL spec evolves, I think I might push to do it as part of this set of changes.

  4. Rule 7 is a big mistake. Name collisions should signal an error unless collision is documented in the source as intended. If the intent is to override, this should require an additional keyword. At one point in the design evolution of the BitC programming language we had a similar ad hoc resolution rule based on ordering that spanned input files in a similar way. It led to highly unpredictable results.

    Order-dependent resolution can be tolerable within a single input unit of compilation (usually a file) when the language specifies LET-bound lexical scoping at top level (that is: top-level forward reference is not allowed without some form of forward declaration), because the ordering is explicit and controlled by the text and so the results are predictable and easily understood. It's really bad across multiple input units, because the order of processing is intrinsically undefined and can change as imports are added and removed in unrelated files.

    But GraphQL doesn't use LET-bound lexical scoping at top level. Because all top level bindings are type bindings and all non-scalar types are reference types, GraphQL handles top-level bindings as if they were LETREC-bound (that is: they permit mutual recursion across types without explicit forward declarations). This is why forward reference in GraphQL is okay. So in GraphQL, order-dependent resolution is a bad idea even in a single file. It's a major change in current semantics, and it's not necessary.

    In languages where the top-level definitions are LET-bound (therefore order-dependent) rather than LETREC-bound, there is a long list of ways to go wrong when re-exports are supported. This is true because re-export followed by import can alter the binding dependency order, which can lead to some interesting semantic problems with data definitions. In LETREC-bound designs the binding order does not matter, but in LET-bound designs it is an essential part of the language semantics that re-export can break. Let's not break that.

Field Import

@zcourts gave overloading as his use case for his rule 7, but that's a matter of how "collision" is defined, and I don't think we want to constrain future evolution of GraphQL in this area. In any case, what he's actually doing is overriding, not overloading. If what you want is a predictable override, one way to do that is to add field renaming to the spec:

type T {
  OtherType.field as LocalFieldName
}

Note that OtherType might be an identifier introduced by an import.

This is semantically consistent, because a field definition in GraphQL is (semantically speaking) a type definition for an accessor or mutator for that field. It also enables something entirely new and very powerful: the ability to build a namespace of published types and fields that creates a clean interface for a larger library having internal structure that would clutter the interface. While it might break existing implementation assumptions, I don't think it even requires any major changes to the GraphqlSchema objects; The main change in graphql-js would be in the parser.

We probably need to restrict these renames for sanity so that we don't end up with input types crossing over with output types. One way is to require that OtherType and T are "directionally compatible". Either both are input types or both are output types. Also, if T is not a mutation type, then the type of OtherType.field cannot be a mutation type (see Nested Mutations below).

Though if I'm being honest, I've never really understood why input types cannot be referenced from an output type. It's an arbitrary constraint that doesn't protect anything so far as I can see. Perhaps not a problem to take up here, though.

Whole Namespace Import

One piece that appears to be missing in the @zcourts proposal is importing namespaces as a whole, such as import LocalName from NamespaceName. The reason this is interesting to examine is that it reveals something important about namespaces: in a type-only language, a namespace is simply a type. There is no semantic difference between the following two things:

namespace MyNamespace {
  scalar UUID
  input type MyInput {...}
  type Query { ... }
  enum MyEnum {...}

  ... possible field definitions ...
}

type MyNamespace {
  scalar UUID
  input type MyInput {...}
  type Query { ... }
  enum MyEnum {...}

  ... possible field definitions ...
}

I'm not opposed to a namespace keyword. We don't actually need a new keyword, but sometimes it is useful to signal intent explicitly. I'm just pointing out that a namespace is actually a type declaration in disguise. Understanding this explains why it is okay to import a namespace as a whole and gives a sensible semantics to that import. It also explains why the concrete change to the GraphqlSchema family of objects is tiny (because import is just name aliasing for an existing GraphqlSchema object), and why the biggest change in this whole thing is dotted name resolution.

A change is needed in the AST and the AST print functionality, but it is smaller than I initially imagined. We would need to introduce a GraphqlTypeAlias object into the GraphqlSchema object family, and a corresponding TypeAliasNode into the AST. When it appears at top level, a name whose type node is GraphqlTypeAlias is an import. When it appears at field level, it is a field cross-reference (my as example above). We only need a GraphqlImport node for the sake of AST re-emission.

All of this is a lot simpler than it sounds, and I believe we are left with a short list of issues:

  1. Dotted name resolution (which I think @zcourts has already addressed).
  2. Documenting why we do not need a way to explicitly reference a top-level type T whose name collides with a lexically shadowing name. That is, we don't need anything like C++ ::T. The reason we don't need this is that types are LETREC-bound. If we want to refer to a top-level type whose name has been shadowed, we can simply import the containing namespace using a non-shadowed name and reference the desired type using the import name.
  3. Document explicitly that imports are only permitted at top level. There is no semantic problem with permitting imports inside namespaces, but it is an unmotivated complication. If we leave it out now, we can add it later without mishap. My recommendation is to take things one step at a time.
  4. Dealing with nested mutations

The last one is important, and I think @zcourts may have overlooked it. It is a subtle issue.

Nested Mutations

According to the GraphQL spec, mutations and queries are treated differently:

  • If the operation is a mutation, the result of the operation is the result of executing the mutation’s top level selection set on the mutation root object type. This selection set should be executed serially, meaning that ExecuteSelectionSet is performed serially.
  • If the operation is a query, the result of the operation is the result of executing the query’s top level selection set with the query root object type. Meaning that ExecuteSelectionSet is performed normally.

The way the spec is written, a mutation operation executes it's top-level selection set against the mutation root object type, which has the effect that it is only permitted to "invoke" fields that are defined in the "Mutation" type. The mutation root object type can be identified in SDL by a schema definition, or by name at query construction. In the current spec, there can only be one Mutation type (globally), so it has not been possible to place mutations in a hierarchical arrangement. If you do, the inner operations will get performed in normal order, which is to say that their order is unspecified. Which means that "later" operations cannot depend on the completion of "earlier" operations. There is a fairly clear explanation of the issue here.

The end result of this is that you can "forge" namespaces for query operations, but not for mutation operations. That approach doesn't provide namespaces for types, and I think the proposal here should be adopted instead. But it does reveal a semantic problem with placing mutations inside another type: we need a more general way to say which types should be taken to contain mutation operations. This is actually a problem for schema stitching and schema merging already, so it's worth looking at how to deal with it.

There is a straightforward way to address this: allow the existing mutation keyword as a qualifier on [output] type definitions. An operation is a mutation operation if (a) its containing type is named as the mutation type at schema construction, or (b) its containing type definition carries a mutation qualifier:

mutation type MyMutations {
   these_are_mutation_operations
}

With this in place, we can sensibly say that dotted names can be used in a mutation, provided that every identifier occurring in the dotted name is a field defined within a mutation-qualified type.

This is a "least change" approach, and it would be pretty easy to implement in graphql-js. It also (largely) eliminates the problem that imports create aliasing, and aliasing creates some ambiguities around identifying mutation types by name.

Note that we do not want to put the mutation annotation at the field level. The entire resolver architecture is predicated on the idea that queries can be resolved in parallel while mutations get resolved sequentially. The processing is defined at the type level, it's current behavior is pretty well understood, and I don't see a particularly good reason to do an overwhelmingly invasive change here.

Note, however, that this proposal does introduce a potential problem. Consider:

mutation type M1 { op1: T1 }
type T1 { nonMutationOp1: M1 nonMutationOp2: M1 }
mutation type M2 { opM2: SomeFinalType }

and a mutation query that references op1 { nonMutationOp1 { opM2 } nonMutationOp2 { opM2 } }. Because T1 is not a mutation type, its selection set is executed "normally", which is to say: the order of execution is undefined. Which means in turn that the order of operation of the two invocations of opM2 is undefined. Which defeats the whole point of the execution ordering rule for mutations.

The fix is to say that a field definition f: MT, where MT is a mutation-qualified type, is only permitted when f appears within a mutation-qualified type. This ensures that the chain of sequential execution for mutations is preserved hierarchically.

Determination of _typename

Once namespaces are introduced, the _typename of a type is it's fully-qualified dotted name at its point of definition. Two types can only be merged if they share a fully-qualified dotted name in common.

This is necessary because of type name collisions, but it introduces a couple of sleeping dragons:

  1. GraphQL itself requires a reserved namespace (I'd suggest 'GraphQL') so that GraphQL-defined types can be correctly unified.
  2. We suddenly end up with a package system. See below:

Open Issues

There are two issues that I haven't thought about at all here:

1. Subscription-qualified types?

Is there any need to have a notion of subscription-qualified type definitions? I don't see one, but...

2. Importing from URL

Is it necessary for the import syntax to permit a URL specifying the imported type, giving us the ability to stitch schemas within the SDL? My personal use-case for this is that it is necessary for dynamically constructed schemas.

Admitting URLs as a right-hand side raises a problem of unification. If two independent schemas A and B import the same schema URL, and those are in turn both imported by schema C, we need to know which types are to be unified. Here are some possible approaches:

  • Types imported through a URL can be merged if they originate from the same concrete import.
  • Types imported through a URL are merged if the URLs are textually identical.
  • Types imported through a URL are merged if they share a common package name (new concept!). That is: types are mergeable if and only if the are defined in the same package and have the same fully qualified name within that package. Package names are "sticky". If type A in package P references type B that does not have an explicitly defined package, then B is deemed to live in package A. The rationale is that we can then do something like the following as an export wrapper:
    package com.yourcompany.yourinterface
    import Query from internal.qualified.name
    import Mutation from internal.qualified.name
    export schema {  // exports as package com.yourcompany.yourinterface
      query: Query
      mutate: Mutation
    }
    
    

I don't feel that I have a particularly good and clear-cut answer to this part, but I think it needs an answer. The main advantage to package names is that they survive if the URL has to change for some reason (e.g. the protocol is re-homed).

3. Extending types from a URL

If a type is important from a URL, what does it mean to extend that type on the importing side? In my opinion, extending a type defined in somebody else's package is a pretty bad idea, and should probably be disallowed. If you are mucking locally with an externally defined type definition, I think there are bigger issues at play.

Request

If someone would be kind enough to point me in the right direction, I'd be happy to write this up more carefully in whatever format the GraphQL community uses for an RFC. I'd also be very happy to collaborate with @zcourts on this. I may have complicated things in ways that are not necessary, and it's always better to get more than one set of experienced eyeballs on this sort of thing.

@b-jsshapiro
Copy link

@b-jsshapiro b-jsshapiro commented Jan 20, 2021

The field import idea that I introduced above does not work. Please ignore.

@Sytten
Copy link

@Sytten Sytten commented Feb 22, 2021

I think we should start a working group to champion the namespaces. It is a big change that won't be done unless there is some organization to write the formal proposal.

@b-jsshapiro
Copy link

@b-jsshapiro b-jsshapiro commented Feb 22, 2021

I think we should start a working group to champion the namespaces. It is a big change that won't be done unless there is some organization to write the formal proposal.

Funny you should say this. @zcourts and I decided several weeks ago to come up with a draft proposal. I'm waiting for his feedback, and I realized that I maybe should rearrange things to make them more clear. Right now it's a Google Doc, but I hope we can turn it into an organized proposal shortly.

@rivantsov
Copy link

@rivantsov rivantsov commented Feb 27, 2021

Hi everybody
I am really interested in the WG and coming RFC and would like to participate/contribute, if only read the early drafts and propose comments, strongly-opinionated as you might expect :)
I have a coming project, POC for now, GraphQL API over big bank system; number of expected types is huge, 5K tables in database plus hundreds of external/internal services with their own models. I don't need to experience it first hand to know that there will be huge problems with that number of types in a single namespace, and there will be huge number of mutations. I know it's not feasible to get it into final spec in this timeframe, but I would be ok with early RFC draft to go with it, even without support from GraphiQL or other clients.

And it's not just me of course. I think this issue is LOOONG overdue, I am surprised to see it lingered for so long; it is clear from this and other posts that this is a huge pain. And simply from experience of working with BIG systems.
I don't buy this argument "We have 10K types at FB and we are OK", we have leaked opinions about ridiculous names out there (see Robert Zhu talk A case against GraphQL https://www.youtube.com/watch?v=djKPtyXhaNE), and comments about other systems - no, these ridiculous names are not OK. We are not OK. It is urgent, past due issue for other folks who already implemented large systems and experienced all the pain and tried various workarounds with quite limited 'success'.

Just wanna induce a sense of urgency. If we want GraphQL to live and prosper, we need to make it friendly for big systems.
I have my own GraphQL server+client implementation in .NET here: https://github.com/rivantsov/ngraphql, that's what I plan to use; I am open for experimentation with SDL changes, playing with syntax is no problem, it is quite easy, just tweak the grammar, so it can be a good playground.
Just suggesting my help cause it is really urgent and important stuff
thank you
Roman

@jxnu-liguobin
Copy link

@jxnu-liguobin jxnu-liguobin commented Apr 22, 2021

I think the / syntax is great ,with the current GraphQL spec,we need mount the on GraphQL ObjectType to another bigger GraphQL object as a field and a new name,like Mount Dummy type to a bigger graph as dummyService field.

pinshen zhe li du you ni .

@LunaticMuch
Copy link

@LunaticMuch LunaticMuch commented Jun 2, 2021

Just wanna induce a sense of urgency. If we want GraphQL to live and prosper, we need to make it friendly for big systems.

I do agree. In my daily work, the lack of namespaces is a challenge. If you create an enterprise endpoint for a big company, or for a company which operates in many sectors, despite all your careful approach with naming, the clash of type is always there waiting for you.
I am not advocating namespaces are needed so everybody can relax and use generic words, rather, because with Agile most of work is done case by case, onboarding and developing pieces of the schema by iterations, it's not always easy to see an object name can eventually clash with another tomorrow.
Nonetheless, if you want to scale parallel schema development across different use cases, you can't end with a strict governance on schema naming which will turn into a single point of conflict as nobody can own its namespace.

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

Successfully merging a pull request may close this issue.

None yet