Mutations

Lets start mutations by defining some Data Transfer Objects (DTO) that will provide our responses from the mutations.

The first is an interface for all mutation responses from our API to provide an ok and errors field.

./DTO/IMutationResponse.cs
using System.Collections.Generic;

namespace SlackClone.DTO
{
    public interface IMutationResponse
    {
        public bool ok { get; set; }
        public List<string> errors { get; set; }
    }
}

Next we will define the DTO for the mutation response.

./DTO/InsertMutationResponse.cs
using System;
using System.Collections.Generic;

namespace SlackClone.DTO
{
    public class InsertMutationResponse : IMutationResponse
    {
        public bool ok { get; set; }
        public List<string> errors { get; set; }
        public Guid InsertedId { get; set; }

        public InsertMutationResponse(bool _ok = false, List<string> _errors = null, Guid _id = default)
        {
            ok = _ok;
            errors = _errors;
            InsertedId = _id;
        }
    }
}

Next we will define the data structure for the inputs we want to receive to perform the mutation.

./DTO/InsertChannelMessageInputs.cs
using System;

namespace SlackClone.DTO
{
    public class InsertChannelMessageInputs
    {
        public string Content { get; set; }
        public string Username { get; set; }
        public Guid ChannelId { get; set; }
    }
}

We can now define the Input Type in the GraphQL schema like below and set the Content field to required.

./GraphQL/Types/InsertChannelMessageInputsType.cs
using SlackClone.DTO;
using HotChocolate.Types;

namespace SlackClone.GraphQL
{
    public class InsertChannelMessageInputsType : 
        InputObjectType<InsertChannelMessageInputs>
    {
        protected override void Configure(
            IInputObjectTypeDescriptor<InsertChannelMessageInputs> descriptor)
        {
            // Sets Field to Required in 
            descriptor.Field(t => t.Content).Type<NonNullType<StringType>>();
        }
    }
}

Now that we have the data structure we need for the input we can go ahead and write the method that will actually perform the mutation and save the data to the database.

./GraphQL/Mutations.cs
using SlackClone.DTO;
using SlackClone.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SlackClone.GraphQL
{
    public class Mutations
    {
        private readonly SlackCloneDbContext dbContext;

        public Mutation(SlackCloneDbContext _dbContext)
        {
            dbContext = _dbContext;
        }

        public InsertMutationResponse InsertChannelMessage(
            InsertChannelMessageInputs data)
        {
            var ok = false;
            var errors = new List<string>();
            var insertedId = default(Guid);
            if (data != null)
            {
                try
                {
                    
                    Channel channel = dbContext.Channels
                        .SingleOrDefault(c => c.Name == "general");

                    ChannelMessage channelMessage = new ChannelMessage
                    {
                        Content = data.Content,
                        CreatedAt = DateTime.UtcNow,
                        Channel = channel
                    };

                    dbContext.ChannelMessages.Add(channelMessage);

                    var cm = new ChannelChannelMessage
                    {
                        Channel = channel,
                        ChannelMessage = channelMessage
                    };
                    
                    channel.ChannelsChannelMessages.Add(cm);

                    dbContext.SaveChanges();
                    insertedId = channelMessage.Id;
                    ok = true;
                }
                catch
                {
                    errors.Add("error occured while creating channel message");
                }
            }

            return new InsertMutationResponse(ok, errors, insertedId);
        }
    }
}
./GraphQL/MutationType.cs
using HotChocolate.Types;


namespace SlackClone.GraphQL
{
    public class MutationType : ObjectType<Mutations>
    {
        protected override void Configure(
            IObjectTypeDescriptor<Mutations> descriptor)
        {
            descriptor.Field(f => f.InsertChannelMessage(default))
                .Argument("data", a => a.Type<InsertChannelMessageInputsType>());
        }
    }
}

Lastly we need to add the MutationType to our GraphQL Schema like seen below on line 39.

./Startup.cs
using SlackClone.Entities;
using SlackClone.GraphQL;
using HotChocolate;
using HotChocolate.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace SlackClone
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. 
        // Use this method to add services to the container.
        // For more information on how to configure your application, 
        // visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEntityFrameworkNpgsql()
                .AddDbContext<SlackCloneDbContext>(opt => 
                    opt.UseNpgsql(
                        Configuration.GetConnectionString("SlackCloneDb")));

            // Adds GraphQL Schema
            services.AddGraphQL(services =>
                SchemaBuilder.New()
                    .AddServices(services)
                    .AddQueryType<QueryType>()
                    .AddMutationType<MutationType>()
                    .Create());
        }

        // This method gets called by the runtime. 
        // Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            // Adds GraphQL Service
            app.UseGraphQL();
            // Adds Playground IDE
            app.UsePlayground();
        }
    }
}

We can view the mutation got added in the Schema by launching the project and viewing the docs in playground.

We can next perform the mutation in the playground like below.

mutation {
  insertChannelMessage(
    data: {
      channelId: "c2c840ca-0b81-4ed1-a4b7-4e150f6ccf22"
      content: "new message"
      username: "dtrump"
    }
  ) {
    ok
    errors
    insertedId
  }
}

If all went well you should see the response below.

{
  "data": {
    "insertChannelMessage": {
      "ok": true,
      "errors": [],
      "insertedId": "97ffc0ac-d8ae-4f51-9469-7353ec4f7a8e"
    }
  }
}

Last updated