Namespacing in query (abstract federation entity)

Hey folks. This is similar to a question that was posted a few days back.

Has anyone found a nice way to extend a non entity in a federated graph? Or extend an entity that isn’t really a concrete entity.

Gor example, i have a domain concept that never has a unique ID but is useful for segregating part of the graph. Let’s call it AbstractConcept.

If two DGSs want to add fields to that AbstractConcept then one of them will need to “own” it for the resolution to work correctly.

I could do that but it means that:

  • I need to pick an arbitrary ID for this AbstractConcept field
  • I need to call the owning DGS always, even if it doesn’t own any of the concrete types being queried for

My solutions here seem to be one of the following:

  1. Remove the abstract type and namespace in another way (from talking to our domain experts this seems incredibly difficult / impossible)
  2. Just accept that one DGS is coupled to that type and make sure it is always available and use some caching
  3. Register the abstract type in the gateway itself so that when that type is asked for, the gateway resolves it and there is no coupling of DGSs (seems like a hack but does at least ensure availability)

Anyone come up against this and solved it?

1 Like
  1. Sorry, but I don’t understand what the question is. Is this about how to write a schema that solves this problem, how to resolve it, or some combination of both of them?
  2. What does this have to do with namespacing?

It’s all to do with namespacing. Let me pick an example:

Say I have a Graph that is based around products. We might have one main DGS that populates most of product X. There is also another DGS that contributes to product X and so it has some top level stuff within that domain.

The users of the products however need the products namespaced within the graph when querying. Some users are more used to one product and some might have multiple products. One way to solve this is to have an abstract “productX” field within our Query:
query {
productX: {
myField
}
}

Product X isn’t an entity, it’s more a logical namespace that makes user consumption easier. However, we want a second DGS to extend that productX to add “anotherField”. This is tricky as far as I can tell.

To go back to my original post with this example in mind -

  1. Maybe we remove the productX concept. This is very difficult if the domain is well established prior to introducing a Graph
  2. Maybe one DGS owns productX. But then anyone querying for just “anotherField” will result in a call to a DGS which actually has no data (the owning DGS), but just resolves the productX for the second DGS. It couples everything under that type to a single DGS (which could potentially be down/slow)
  3. The gateway “owns” this and any other abstract type. The gateway registers those types and resolves them itself so that when a query comes in it just calls itself to resolve productX and then the usual GraphQL pattern of only calling services that need to be called resumes for any child under an abstract type.

I’m curious whether anyone has come up against a similar issue and how they solved it. It seems like it’s inevitable.

You keep using the term DGS. What does this mean?

As for your description of what you want to do, this is too abstract for me. What do you actually want your schema to look like?

Because as it stands, it seems like you’re asking for runtime control of the schema, and others, especially Apollo, seem to be moving as far as possible away from startup/runtime schemas because they’re not particularly fast, predictable, or stable for production environments.

If you are talking about a static schema, “namespacing” (not dynamic namespacing) is easy.

type MyNamespace {
  _unused: String @deprecated("used to establish root object")
}

extend type Query {
  myNamespace: MyNamespace
}
const resolvers = {
  Query: {
    myNamespace: () => ({}),
  }

After that, you can extend MyNamespace at will and use 100% normal resolvers. I do this all the time since Apollo doesn’t support dynamic namespacing right now.

So DGS = Domain Graph Service. Apologies I’m using a Netflix term for it but I thought it was fairly well known in the GraphQL Federation circles.

Your example is fine for a single Apollo server but I’m trying to do this with a Federated Gateway, with multiple graph services (DGS) extending the namespace.

type MyNamespace {
  someField: String
}

extend type MyNamespace {
  anotherField: String
}

The above, where the extension is happening in a second GraphQL service.

The actual implementation details aren’t as much of a concern for me. I’m more interested in the schema design and whether the Apollo federation framework has some stuff in it to help with this that I’ve overlooked.

The actual graph service that might be extending the namespace type could be written in any language but it will support the Apollo Federation spec.

1 Like

Apologies I’m using a Netflix term for it but I thought it was fairly well known in the GraphQL Federation circles.

All good; I figured, I just normally hear people refer to them as child services.

Your example is fine for a single Apollo server but I’m trying to do this with a Federated Gateway, with multiple graph services (DGS) extending the namespace.

I use this in federation, but I don’t normally let multiple services define the same namespace. If your services expect a set of namespaces and add to them, but none specifically own the namespace, then it would probably be best to have a “namespace” service that simply defines empty types and the other services can extend them at will.

It’s like the same concept as the root Query type. The gateway creates it, which is why federated services all extend it.

You just need a service to “own” the namespace, just like any type. It doesn’t actually have to do anything.

A namespace is just a type; any federated service can define a type and any other federated service can extend said type.

Thanks yea I came to a similar conclusion except I think it probably makes more sense for the namespaces to live in the gateway itself. I’m worried about resiliency (and latency) if a separate service owns the namespaces.

I was hoping there would be something innate in the framework to help with this that I missed but possibly not.

1 Like