diff --git a/juniper_graphql_ws/CHANGELOG.md b/juniper_graphql_ws/CHANGELOG.md
index 05232472..51a7a654 100644
--- a/juniper_graphql_ws/CHANGELOG.md
+++ b/juniper_graphql_ws/CHANGELOG.md
@@ -1,3 +1,4 @@
 # master
 
+- Fix null deserialization issue ([#735](https://github.com/graphql-rust/juniper/issues/735))
 - Initial Release
diff --git a/juniper_graphql_ws/src/client_message.rs b/juniper_graphql_ws/src/client_message.rs
index 1e20caef..8d826819 100644
--- a/juniper_graphql_ws/src/client_message.rs
+++ b/juniper_graphql_ws/src/client_message.rs
@@ -1,3 +1,4 @@
+use crate::utils::default_for_null;
 use juniper::{ScalarValue, Variables};
 
 /// The payload for a client's "start" message. This triggers execution of a query, mutation, or
@@ -10,7 +11,7 @@ pub struct StartPayload<S: ScalarValue> {
     pub query: String,
 
     /// The optional variables.
-    #[serde(default)]
+    #[serde(default, deserialize_with = "default_for_null")]
     pub variables: Variables<S>,
 
     /// The optional operation name (required if the document contains multiple operations).
@@ -27,7 +28,7 @@ pub enum ClientMessage<S: ScalarValue> {
     ConnectionInit {
         /// Optional parameters of any type sent from the client. These are often used for
         /// authentication.
-        #[serde(default)]
+        #[serde(default, deserialize_with = "default_for_null")]
         payload: Variables<S>,
     },
     /// Start messages are used to execute a GraphQL operation.
@@ -128,4 +129,20 @@ mod test {
             serde_json::from_str(r##"{"type": "connection_terminate"}"##).unwrap(),
         );
     }
+
+    #[test]
+    fn test_deserialization_of_null() -> serde_json::Result<()> {
+        let payload = r#"{"query":"query","variables":null}"#;
+        let payload: StartPayload<DefaultScalarValue> = serde_json::from_str(payload)?;
+
+        let expected = StartPayload {
+            query: "query".into(),
+            variables: Variables::default(),
+            operation_name: None,
+        };
+
+        assert_eq!(expected, payload);
+
+        Ok(())
+    }
 }
diff --git a/juniper_graphql_ws/src/lib.rs b/juniper_graphql_ws/src/lib.rs
index 7cf73eec..fbbb7565 100644
--- a/juniper_graphql_ws/src/lib.rs
+++ b/juniper_graphql_ws/src/lib.rs
@@ -21,6 +21,8 @@ pub use server_message::*;
 mod schema;
 pub use schema::*;
 
+mod utils;
+
 use juniper::{
     futures::{
         channel::oneshot,
diff --git a/juniper_graphql_ws/src/utils.rs b/juniper_graphql_ws/src/utils.rs
new file mode 100644
index 00000000..75106a4c
--- /dev/null
+++ b/juniper_graphql_ws/src/utils.rs
@@ -0,0 +1,9 @@
+use serde::{Deserialize, Deserializer};
+
+pub(crate) fn default_for_null<'de, D, T>(deserializer: D) -> Result<T, D::Error>
+where
+    D: Deserializer<'de>,
+    T: Deserialize<'de> + Default,
+{
+    Ok(Option::<T>::deserialize(deserializer)?.unwrap_or_default())
+}