Giter Club home page Giter Club logo

Comments (5)

kitten avatar kitten commented on May 26, 2024

It's a little hard here to differentiate between what you're expecting, what you're trying to do, what your use-case is, and what you proposed as a solution, so maybe you pre-empted this issue description a little, since it combines a lot of what I mentioned at once. But I'll give it my best to describe what I think are some angles to a solution here that already work today ❤️

Re. parent-to-child relations. It's unclear to me what you mean with the disconnect between parent and child. Any field key is irrelevant here, because that's just a relation and the normalisation happens on entities (i.e. entity keys).

This means that, typically, if you have a parent-child relation (be it 1:N, or 1:1, etc), you can create edge objects that help with normalisation of fields on the relation itself. This is a really underappreciated pattern in schema design, and helps defend yourself against denormalised schemas that don't accurately reflect some UI dependencies and data.

For example,

type User {
  # ...
  likedBooks(someFilter: FilterInput): [Book]
}

type Book {
  # let's say this is unkeyable because of the following field:
  likedByUserAt: DateTime!
}

This often leads to issues compared to:

type User {
  # ...
  likedBooks(someFilter: FilterInput): [LikedBookEdge!]
}

type LikedBookEdge {
  book: Book!
  user: User!
  likedByUserAt: DateTime!
}

type Book {
  id: ID!
}

in this example, Book and User are completely independent from their directional edge-field likedByUserAt, so the UI and cache are able to differentiate, even when multiple users and a single book have multiple ways to connect to each other. (N:1 reverse case, basically)

But, it's hard to tell for me whether this is applicable to your situation. Specifically,

Now we have a detail component that loads all fields without providing the include parameter and a Table component that only loads a subset of the fields.

^this section doesn't tell me enough about why your schema and the normalised cache together aren't working out in your specific schema design case.

Passing the parent to the keys function would allow construction of a composite key for a Field

This is actually already possible. We recently have loosened some restrictions to create a blanket solution for these problems.

You can now pass the globalIDs config to Graphcache, which basically allows you to remove __typename from entity keys and hence lift restrictions that stop you from creating overlapping keys: https://commerce.nearform.com/open-source/urql/docs/api/graphcache/#cacheexchange

That said, I'd still recommend using Edge-type objects schema design, because:

{ Field: (field, parent) => entity.id + parent.key }

is a strong indication that globalIds aren't sufficient because your schema design necessitates treating objects as denormalised.

from urql.

BickelLukas avatar BickelLukas commented on May 26, 2024

@kitten Thanks for the quick reply.
I understand your approach with edges but I'm not quite sure if that's applicable here.

Here's some more context on what we're building:
In our system, administrators can dynamically add fields to an entity. You can imagine it kind of like a CMS where the administrator designs the schema and the users can then fill in data.

Without graphql the api could just return the fields directly in the object:

{
  "id": 1,

  "title": "This is a title",
  "description": "detail text",
  ... dynamically added
}

but since graphql requires us to specify the exact schema of the data which is not known ahead of time we opted to return the fields as a list.

{
  "id": 1,
  "fields": [
    { "key": "title", "value": "This is a title" },
    { "key": "description", "value": "detail text" },
    ... dynamically added
  ]
}

so the fields collection is not an edge to another entity but rather just a way to expose dynamic data via graphql.

Now when we execute the following 2 queries, I would want the same value for the "title" field to be returned for both queries.

query CompleteEntity($id: ID!) {
  entity(id: $id) {
    id
    fields {
      key
      value
    }
  }
}

query PartialEntity($id: ID!) {
  entity(id: $id) {
    id
    fields(include: ["title"] {
      key
      value
    }
  }
}

You can now pass the globalIDs config to Graphcache, which basically allows you to remove __typename from entity keys and hence lift restrictions that stop you from creating overlapping keys: https://commerce.nearform.com/open-source/urql/docs/api/graphcache/#cacheexchange

We are actually already using globalIDs and our API follows the relay schema.

from urql.

JoviDeCroock avatar JoviDeCroock commented on May 26, 2024

Reading over this I wonder why that's not being returned as the JSON sacalar. That being said, this could just be an embedded entity by keying with () => null as arbitrary data like that shouldn't be subject to updates either way

from urql.

kitten avatar kitten commented on May 26, 2024

Edges can still help here, because you can have entity -> fields(...) -> field edge -> field where field edge has a unique ID specific to both entity and field. Or alternatively, field has an ID field that both identifies the parent and the field key.

This by definition though is denormalised data otherwise. That's comparable to having a Field table in a database that doesn't have a field to identify the Entity.

The problem with customising embedded entity keys is that, instead of forcing good schema patterns, we'd make a case that's hard to configure and hard to customise, in that, this would have to be separate from keys and only trigger when a certain parent-child relationship occurs.
Which would then likely cascade into feature requests for grandparent-parent-child relationships.

from urql.

BickelLukas avatar BickelLukas commented on May 26, 2024

It doesn't quite feel right, but I can solve it by adding the entityKey to the Field type like you suggested.
Thanks!

from urql.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.