Create todos - mutation
In this part of the tutorial, you will learn how to create new todos by using GraphQL Mutations.
Let's define a graphql mutation to insert into todos.
mutation ($todo: String!, $isPublic: Boolean!) {insert_todos(objects: {title: $todo, is_public: $isPublic}) {affected_rows}}
You will also need to pass in the values for the variables.
Try this mutation in GraphiQL against the application database to see what the response looks like.
Let's now integrate this graphql mutation into our elm app.
We will construct a GraphQL Mutation to insert a private todo and integrate it with the elm app.
Configure GraphQL Client
We need a client which can take a GraphQL mutation, make a network call and return a GraphQL response. elm-graphql package exposes a light weight http client which can be used to make GraphQL requests. Lets configure it to our requirements
Open src/GraphQLClient.elm
and add the following code:
- module GraphQLClient exposing (makeGraphQLQuery)+module GraphQLClient exposing (makeGraphQLMutation, makeGraphQLQuery)import Graphql.Http- import Graphql.Operation exposing (RootQuery)+import Graphql.Operation exposing (RootMutation, RootQuery)import Graphql.SelectionSet as SelectionSet exposing (SelectionSet)+makeGraphQLMutation : String -> SelectionSet decodesTo RootMutation -> (Result (Graphql.Http.Error decodesTo) decodesTo -> msg) -> Cmd msg+makeGraphQLMutation authToken query decodesTo =+ query+ |> Graphql.Http.mutationRequest graphql_url+ {-+ mutationRequest signature is of the form+ String -> SelectionSet decodesTo RootMutation -> Request decodesTo+ url -> SelectionSet TasksWUser RootMutation -> Request TasksWUser+ -}+ |> getAuthHeader authToken+ |> Graphql.Http.send decodesTo
Import dependencies
Lets import the types, utility functions generated by elm-graphql into our app and construct a GraphQL query
Open src/Main.elm
and add the following code:
- import GraphQLClient exposing (makeGraphQLQuery)+import GraphQLClient exposing (makeGraphQLMutation, makeGraphQLQuery)import Graphql.Http- import Graphql.Operation exposing (RootQuery)+import Graphql.Operation exposing (RootMutation, RootQuery)import Hasura.InputObjectexposing( Boolean_comparison_exp, Todos_bool_exp+ , Todos_insert_input, Todos_order_by, buildBoolean_comparison_exp, buildTodos_bool_exp+ , buildTodos_insert_input, buildTodos_order_by)+import Hasura.Mutation as Mutation+ exposing+ ( InsertTodosRequiredArguments+ , insert_todos+ )+import Hasura.Object.Todos_mutation_response as TodosMutationimport Html.Eventsexposing( onClick, onInput, keyCode, on+ , onSubmit)
Construct GraphQL Mutation
fetchPrivateTodos : String -> Cmd MsgfetchPrivateTodos authToken =makeGraphQLQuery authTokenfetchPrivateTodosQuery(RemoteData.fromResult >> FetchPrivateDataSuccess)+insertTodoObjects : String -> Bool -> Todos_insert_input+insertTodoObjects newTodo isPublic =+ buildTodos_insert_input+ (\args ->+ { args+ | title = Present newTodo+ , is_public = Present isPublic+ }+ )+++insertArgs : String -> Bool -> InsertTodosRequiredArguments+insertArgs newTodo isPublic =+ InsertTodosRequiredArguments [ insertTodoObjects newTodo isPublic ]+++getTodoListInsertObject : String -> Bool -> SelectionSet (Maybe MutationResponse) RootMutation+getTodoListInsertObject newTodo isPublic =+ insert_todos identity (insertArgs newTodo isPublic) mutationResponseSelection+++mutationResponseSelection : SelectionSet MutationResponse Hasura.Object.Todos_mutation_response+mutationResponseSelection =+ SelectionSet.map MutationResponse+ TodosMutation.affected_rows+++makeMutation : SelectionSet (Maybe MutationResponse) RootMutation -> String -> Cmd Msg+makeMutation mutation authToken =+ makeGraphQLMutation authToken mutation (RemoteData.fromResult >> GraphQLResponse >> InsertPrivateTodoResponse)
Add Data Types
Lets add new data types required to perform this operation
type alias OnlineUser ={ id : String, user : User}+type alias MutationResponse =+ { affected_rows : Int+ }+++type alias MaybeMutationResponse =+ Maybe MutationResponse+++type GraphQLResponse decodesTo+ = GraphQLResponse (RemoteData (Graphql.Http.Error decodesTo) decodesTo)type alias PrivateTodo ={ todos : TodoData, visibility : String, newTodo : String+ , mutateTodo : GraphQLResponse MaybeMutationResponse}initializePrivateTodo : PrivateTodoinitializePrivateTodo ={ todos = RemoteData.Loading, visibility = "All", newTodo = ""+ , mutateTodo = GraphQLResponse RemoteData.NotAsked}
Add new Msg type
Lets add Msg
types required to perform this operation
type Msg= EnteredEmail String| EnteredPassword String| EnteredUsername String| MakeLoginRequest| MakeSignupRequest| ToggleAuthForm DisplayForm| GotLoginResponse LoginResponseParser| GotSignupResponse SignupResponseParser| ClearAuthToken| FetchPrivateDataSuccess TodoData+ | InsertPrivateTodo+ | UpdateNewTodo String+ | InsertPrivateTodoResponse (GraphQLResponse MaybeMutationResponse)
Handle new Msg types in update
FetchPrivateDataSuccess response ->updatePrivateData (\privateData -> { privateData | todos = response }) model Cmd.none+ InsertPrivateTodoResponse response ->+ updatePrivateData (\privateData -> { privateData | mutateTodo = response, newTodo = "" }) model (fetchPrivateTodos model.authData.authToken)++ InsertPrivateTodo ->+ case String.length model.privateData.newTodo of+ 0 ->+ ( model, Cmd.none )++ _ ->+ let+ mutationObj =+ getTodoListInsertObject model.privateData.newTodo False+ in+ updatePrivateData (\privateData -> { privateData | mutateTodo = GraphQLResponse RemoteData.Loading }) model (makeMutation mutationObj model.authData.authToken)++ UpdateNewTodo newTodo ->+ updatePrivateData (\privateData -> { privateData | newTodo = newTodo }) model Cmd.none
Update render functions
renderTodos : PrivateTodo -> Html MsgrenderTodos privateData =div [ class "tasks_wrapper" ] <|case privateData.todos ofRemoteData.NotAsked ->[ text "" ]RemoteData.Success todos ->[ todoListWrapper privateData.visibility todos ]RemoteData.Loading ->[ span [ class "loading_text" ][ text "Loading todos ..." ]]RemoteData.Failure err ->[ text "Error loading todos" ]+handleMutationTodo : GraphQLResponse MaybeMutationResponse -> List (Html msg)+handleMutationTodo (GraphQLResponse mutationTodo) =+ case mutationTodo of+ RemoteData.NotAsked ->+ [ text "" ]++ RemoteData.Success todos ->+ [ text "" ]++ RemoteData.Loading ->+ [ i [ class "fa fa-spinner fa-spin" ] []+ ]++ RemoteData.Failure err ->+ [ text "Error Mutating data:" ]+++todoMutation : GraphQLResponse MaybeMutationResponse -> Html msg+todoMutation mutateTodo =+ span [ class "mutation_loader" ] <|+ handleMutationTodo mutateTodopersonalTodos : PrivateTodo -> Html MsgpersonalTodos privateData =div [ class "col-xs-12 col-md-6 sliderMenu p-30" ][ div [ class "todoWrapper" ][ div [ class "sectionHeader" ][ text "Personal todos"]- , form [ class "formInput" ]+ , form [ class "formInput", onSubmit InsertPrivateTodo ]- [ input [ class "input", placeholder "What needs to be done?" ]+ [ input [ class "input", placeholder "What needs to be done?", onInput UpdateNewTodo, value privateData.newTodo ][], i [ class "inputMarker fa fa-angle-right" ] []+ , todoMutation privateData.mutateTodo], renderTodos privateData]]
How does this work?
Here is the summary of how this works
- User types
todo
in the input box - OnSubmit invokes the configured function, which returns a type
Msg
- The update function is configured to listen to
Msg
types and hence the mutation is executed
Woot! You have successfully written your first GraphQL mutation with Elm. Easy isn't it?
- Build apps and APIs 10x faster
- Built-in authorization and caching
- 8x more performant than hand-rolled APIs