Git Product home page Git Product logo

hotchocolate-polymorphic-ids's Introduction

Polymorphic IDs

This package adds support to ChilliCream's HotChocolate for polymorphic Relay / Global IDs so that you can pass the database id in as an ID in an input/argument and it'll be accepted.

For example, if you read the description of an ID type in GitHub's GraphQL API, it says:

When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.

The following becomes possible (on args/input fields annotated with HotChocolate's [ID] attribute).

# Schema
type Query {
  booking(id: ID!): Booking
}

# Query
query {
  bookingByGlobalId: booking(id: "TheGlobalIdValue7sghdyg=") { ... }
  
  bookingByDbId: booking(id: 1) { ... }
  
  bookingByDbIdString: booking(id: "1") { ... }
}

More details

Why would you do this?

  1. To achieve friendly URLs, like /booking/123, you need to be able to get a booking by its database id (123) as the client doesn't have the global ID. But it's nasty to have to expose a bookingByDbId(id: Int!) field to do so.
  2. For easier debugging. As humans we use database ids. So if you've got one, you can just pass it on through.

What's supported?

Currently only arguments / input fields that are annotated with the [ID] attribute will be noticed and have support added for handling different ids. Specifically, the fluent-style declaration .ID() won't be handled right now; though support could be added for it in the future so shout out if you need it on the open issue.

Arrays of IDs are handled (but only v2+ can support arrays of nullable IDs ([ID] or [ID]!) due to a bug in Hot Chocolate v11).

IDs that are internally represented with int, Guid, long or string, and their nullable equivalents will be handled. You can opt-out of each's support as required.

For integer-based IDs, you can pass "1" or 1 and both will be accepted.

For all other types, you need to pass the string value, e.g.

  • "26a2dc8f-4dab-408c-88c6-523a0a89a2b5" for a guid-based ID
  • "123456789" for a long-based ID

Any downsides?

  1. Strings are a problem. It's difficult to distinguish between the global id format and a string database id. As such, in this case, we try to read it as a global id and if that throws we consider it a database id. The one problem being that invalid global ids, e.g. you missed one char, will be considered a database id. If you don't have string db ids, it's a good idea to just turn off their handling so an invalid global id would still throw (see setup below).
  2. There's a performance hit to the interception, but it'd be barely measurable.
  3. Once you go down this path, it's very difficult to go back as your clients will start to rely on this.

Setup

Install a compatible version of the package from NuGet

dotnet add package AutoGuru.HotChocolate.PolymorphicIds

Configure it on your schema (ISchemaBuilder) or executor (IRequestExecutorBuilder):

.AddGlobalObjectIdentification() // Required since Hot Chocolate v12.6.0+
.AddPolymorphicIds(new PolymorphicIdsOptions
{
    HandleGuidIds = false,    // true by default
    HandleIntIds = true,      // true by default
    HandleLongIds = false,    // true by default
    HandleStringIds = false,  // true by default
});

Adding a dbId field declaratively

At AutoGuru we add a dbId field to all our nodes. Since this is essentially the same as declaring the node's id field, we've got some helpers for this that can be found here.

Currently this is a manual/explicit thing you need to do, but in future this will ideally become automatic via a type interceptor if I can figure out how to get that to work (last attempt failed :P).

Compatibility

We depend on HotChocolate.Execution which can bring breaking changes from time to time and require a major bump our end. Compatibility is listed below.

We strive to match Hot Chocolate's supported .NET target frameworks, though this might not always be possible.

HotChocolate Polymorphic IDs Our docs
v13.0.0 v4 right here
v12.6.0* v3 /v3/main branch
v12.0.0 v2 /v2/main branch
v11.1.0 v1 /v1/main branch

* Denotes unexpected binary incompatibility / breaking change in Hot Chocolate

hotchocolate-polymorphic-ids's People

Contributors

ahanoff avatar benmccallum avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

hotchocolate-polymorphic-ids's Issues

Upgraded Hot Chocolate to 12.4.1: Unable to resolve type from field `includeDeprecated`

HotChocolate.SchemaException: For more details look at the `Errors` property.

1.Unable to resolve type from field `includeDeprecated`. (HotChocolate.Types.Introspection.__Type)

  at AutoGuru.HotChocolate.Types.Relay.PolymorphicIdsTypeInterceptor.GetIdInfo(ITypeCompletionContext completionContext, ArgumentDefinition definition)
   at AutoGuru.HotChocolate.Types.Relay.PolymorphicIdsTypeInterceptor.OnBeforeCompleteType(ITypeCompletionContext completionContext, DefinitionBase definition, IDictionary`2 contextData)
   at HotChocolate.Configuration.AggregateTypeInterceptor.OnBeforeCompleteType(ITypeCompletionContext completionContext, DefinitionBase definition, IDictionary`2 contextData)
   at HotChocolate.Types.TypeSystemObjectBase`1.OnBeforeCompleteType(ITypeCompletionContext context, DefinitionBase definition, IDictionary`2 contextData)
   at HotChocolate.Types.TypeSystemObjectBase`1.CompleteType(ITypeCompletionContext context)
   at HotChocolate.Configuration.TypeInitializer.<CompleteTypes>g__CompleteType|27_0(RegisteredType registeredType)
   at HotChocolate.Configuration.TypeInitializer.ProcessTypes(TypeDependencyKind kind, Func`2 action)
   at HotChocolate.Configuration.TypeInitializer.CompleteTypes()
   at HotChocolate.Configuration.TypeInitializer.Initialize()
   at HotChocolate.SchemaBuilder.Setup.InitializeTypes(SchemaBuilder builder, IDescriptorContext context, IReadOnlyList`1 types, LazySchema lazySchema)
   at HotChocolate.SchemaBuilder.Setup.Create(SchemaBuilder builder, LazySchema lazySchema, IDescriptorContext context)
   at HotChocolate.SchemaBuilder.Create(IDescriptorContext context)
   at HotChocolate.SchemaBuilder.HotChocolate.ISchemaBuilder.Create(IDescriptorContext context)
   at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaAsync(NameString schemaName, RequestExecutorSetup options, RequestExecutorOptions executorOptions, IServiceProvider serviceProvider, TypeModuleChangeMonitor typeModuleChangeMonitor, CancellationToken cancellationToken)
   at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaServicesAsync(NameString schemaName, RequestExecutorSetup options, CancellationToken cancellationToken)
   at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorNoLockAsync(NameString schemaName, CancellationToken cancellationToken)
   at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorAsync(NameString schemaName, CancellationToken cancellationToken)
   at HotChocolate.Execution.RequestExecutorProxy.GetRequestExecutorAsync(CancellationToken cancellationToken)
   at HotChocolate.AspNetCore.HttpPostMiddleware.HandleRequestAsync(HttpContext context, AllowedContentType contentType)
   at HotChocolate.AspNetCore.HttpPostMiddleware.InvokeAsync(HttpContext context)
   at HotChocolate.AspNetCore.WebSocketSubscriptionMiddleware.InvokeAsync(HttpContext context)
   at Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.<>c__DisplayClass13_0.<<UseCancellation>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Is It Possible To Get This to Work With MongoDB's ObjectId Type

Thanks for the excellent library! Polymorphic ids is such a great idea!

Is it possible to get this to work with MongoDB's MongoDB.Bson.ObjectId type? Or, is it even a good idea to use ObjectId as an ID field?

    [NodeEx]
    public class SomeEntity
    {
        /// <summary>
        /// Id that uniquely identifies this entity
        /// </summary>
        [ID]
        public ObjectId Id { get; set; }


        [Required]
        public string? Name { get; set; }

        public string? Description { get; set; }

        public static async Task<SomeEntity> Get([ID] ObjectId id,
            [Service] SomeEntityDataLoader service)
        {
            var node= await service.LoadAsync(id);
            return node;
        }
    }

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.