What’s Wrong With GraphQL?

Written by sstcvetkov | Published 2023/02/21
Tech Story Tags: graphql | dotnet | c-sharp | programming | coding | api | graphql-api | data

TLDRGraphQL can add some overhead to your API requests, as each request needs to be parsed and validated before it can be executed. Nullable types are a common issue when working with GraphQL for C# developers. You can use nullable types in your GraphQL schema to indicate that a field can be null. C# doesn’t have namespaces.via the TL;DR App

While GraphQL offers several benefits, there are some potential disadvantages and challenges to using it in C# to consider, before you decide to implement it.

These include:

Complexity

GraphQL requires a new way of thinking and implementing APIs in general. This can lead to a steep learning curve. Especially for developers who are used to working with REST APIs.

GraphQL adds an additional layer of complexity to your codebase, making it harder to maintain and debug. While there are many libraries available for C# .NET, they are usually not as mature or well-supported as those available for other programming languages.

Caching is also more complex with GraphQL, as each query can potentially return different data depending on the requested fields. This makes it harder to implement efficient caching strategies.

GraphQL's flexibility can make it harder to secure your API, especially if you have different access rules for your entities. You'll need to ensure that users can only access the data they're authorized to see.

Performance overhead

GraphQL can add some overhead to your API requests, as each request needs to be parsed and validated before it can be executed. This makes a noticeable impact if the server is receiving a large number of queries or if the queries are very complex. It may be important to optimize the parsing and validation process, for example by using caching or optimizing the query execution plan.

Another potential performance overhead is related to the execution of the GraphQL resolvers. In C#, these are typically implemented using methods that return the requested data. If the resolvers are not optimized, or if they require expensive operations like database queries, they can significantly impact the overall performance of the API. It can be avoided by carefully designing the resolver functions and optimizing their performance as much as possible.

Nullability

Nullable types are a common issue when working with GraphQL for C# developers. This is because GraphQL has a non-nullability by default. The whole type system is based on that.

In C#, however, nullability is just a feature of the language, and many types and properties are nullable by default. It’s causing some confusion when mapping C# types to GraphQL types. To address this issue, you can use nullable types in your GraphQL schema to indicate that a field can be null.

For example, in your C# class, you might have a property like this:

public string? Name { get; set; }

The ? indicates that the Name property can be null. To map this to a GraphQL type, you would need to use the NonNullGraphType class to specify that the field is nullable.

For example:

Field<StringGraphType>("name", resolve: context => context.Source.Name);

In this example, the StringGraphType is wrapped in a NonNullGraphType, which indicates that the name field can be null. Alternatively, you can use the NullableGraphType class to specify that a field is nullable:

Field<NullableGraphType<StringGraphType>>("name", resolve: context => context.Source.Name);

The NullableGraphType wraps the StringGraphType to indicate that the name field can be null.

By using these techniques, you can ensure that your GraphQL schema accurately reflects the nullability of your C# types and properties.

Namespaces

GraphQL basically doesn’t have namespaces. It requires that you specify the fully-qualified name of a type, which includes the namespace. If you're not careful when defining your types and fields, you may end up with naming conflicts or difficulties with resolving the correct namespace. This is funny but mostly sad.

You can try to avoid namespace issues in GraphQL C#, you can use the ResolveType method to specify the correct type for a given GraphQL object. This method is used to determine the appropriate C# type for a given GraphQL type, which can help you avoid naming conflicts and namespace issues.

Here's an example of how to use the ResolveType method:

public class MySchema : Schema
{
  public MySchema(IDependencyResolver resolver) : base(resolver)
  {
    Query = resolver.Resolve<MyQuery>();
    RegisterType<PersonType>();
    RegisterType<CompanyType>();
    RegisterType<UnionGraphType>();
  }
}
public override Type ResolveType(string typeName)
{
    switch (typeName)
    {
        case "Person":
            return typeof(PersonType);
        case "Company":
            return typeof(CompanyType);
        case "Union":
            return typeof(UnionGraphType);
        default:
            throw new ArgumentOutOfRangeException(nameof(typeName), $"Type {typeName} not found.");
    }
}

In this example, the ResolveType method is used to map the GraphQL type names to the corresponding C# types. This ensures that the correct namespace is used for each type, avoiding any potential naming conflicts.

In GraphQL, it's a good practice to use unique names for your types and fields, to avoid any possible conflicts with other namespaces in your application.

Generics

Generics can sometimes present a challenge when working with GraphQL in C# because GraphQL requires a strict type system. In particular, GraphQL does not support open generic types.

One solution to this issue is to use a closed generic type in your GraphQL schema, where the type arguments are explicitly defined. Here's an example of how to use a closed generic type in a

public class MySchema : Schema
{
  public MySchema(IDependencyResolver resolver) : base(resolver)
  {
    Query = resolver.Resolve<MyQuery>();
    RegisterType<ListGraphType<PersonType>>();
  }
}

In this example, the ListGraphType is a closed generic type that represents a list of PersonType objects. The type argument PersonType is explicitly defined, which allows GraphQL to properly interpret the schema.

If you need to use a generic type that can accept any type argument, you can use a non-generic interface as the type in your GraphQL schema. Then, you can use a custom resolver to map the interface to the appropriate concrete type at runtime.

Using closed generic types and custom resolvers are two common techniques for working with generics in GraphQL C#.

The end

Many of these challenges can be mitigated with proper planning and implementation. With careful optimization and design, it is possible to minimize this overhead and achieve performance GraphQL APIs in C#, but it will cost you some work.


Lead image generated with Giphy on HackerNoon.


Written by sstcvetkov | A web developer/content writer/tech speaker
Published by HackerNoon on 2023/02/21