diff --git a/ui/next/app/redux/databaseInfo.spec.ts b/ui/next/app/redux/databaseInfo.spec.ts
index 912d73643f4f..c69d5f0157cc 100644
--- a/ui/next/app/redux/databaseInfo.spec.ts
+++ b/ui/next/app/redux/databaseInfo.spec.ts
@@ -375,7 +375,7 @@ describe("databases reducers", function () {
 
           return {
             sendAsJson: false,
-            body: response.encodeJSON(),
+            body: response.toArrayBuffer(),
           };
         });
 
@@ -446,7 +446,7 @@ describe("databases reducers", function () {
 
             return {
               sendAsJson: false,
-              body: response.encodeJSON(),
+              body: response.toArrayBuffer(),
             };
           });
 
@@ -552,7 +552,7 @@ describe("databases reducers", function () {
 
             return {
               sendAsJson: false,
-              body: response.encodeJSON(),
+              body: response.toArrayBuffer(),
             };
           });
 
diff --git a/ui/next/app/redux/metrics.spec.ts b/ui/next/app/redux/metrics.spec.ts
index a38beef5d06c..304a46a1574f 100644
--- a/ui/next/app/redux/metrics.spec.ts
+++ b/ui/next/app/redux/metrics.spec.ts
@@ -159,9 +159,9 @@ describe("metrics reducer", function() {
       fetchMock.mock("/ts/query", "post", (url: string, requestObj: RequestInit) => {
           // Assert that metric store's "inFlight" is 1 or 2.
           assert.isAtLeast(mockMetricsState.inFlight, 1);
-          assert.isAtMost(mockMetricsState.inFlight, 1);
+          assert.isAtMost(mockMetricsState.inFlight, 2);
 
-          let request = new protos.cockroach.ts.tspb.TimeSeriesQueryRequest(JSON.parse(requestObj.body as string));
+          let request = protos.cockroach.ts.tspb.TimeSeriesQueryRequest.decode(requestObj.body as ArrayBuffer);
 
           return {
             sendAsJson: false,
@@ -172,7 +172,7 @@ describe("metrics reducer", function() {
                   datapoints: [],
                 };
               }),
-            }).encodeJSON(),
+            }).toArrayBuffer(),
           };
       });
 
@@ -232,7 +232,7 @@ describe("metrics reducer", function() {
           }
           successSent = true;
 
-          let request = new protos.cockroach.ts.tspb.TimeSeriesQueryRequest(JSON.parse(requestObj.body as string));
+          let request = protos.cockroach.ts.tspb.TimeSeriesQueryRequest.decode(requestObj.body as ArrayBuffer);
 
           return {
             sendAsJson: false,
@@ -243,7 +243,7 @@ describe("metrics reducer", function() {
                   datapoints: [],
                 };
               }),
-            }).encodeJSON(),
+            }).toArrayBuffer(),
           };
       });
 
diff --git a/ui/next/app/redux/raft.spec.ts b/ui/next/app/redux/raft.spec.ts
index 94c599bd35db..ab4e488d57c2 100644
--- a/ui/next/app/redux/raft.spec.ts
+++ b/ui/next/app/redux/raft.spec.ts
@@ -96,7 +96,7 @@ describe("raft reducer", function() {
           successSent = true;
           return {
             sendAsJson: false,
-            body: new protos.cockroach.server.serverpb.RaftDebugResponse().encodeJSON(),
+            body: new protos.cockroach.server.serverpb.RaftDebugResponse().toArrayBuffer(),
           };
       });
 
diff --git a/ui/next/app/redux/uiData.spec.ts b/ui/next/app/redux/uiData.spec.ts
index 805f69e8b289..44bd9c30d00a 100644
--- a/ui/next/app/redux/uiData.spec.ts
+++ b/ui/next/app/redux/uiData.spec.ts
@@ -124,12 +124,12 @@ describe("UIData reducer", function() {
       fetchMock.mock("/_admin/v1/uidata", "post", (url: string, requestObj: RequestInit) => {
         assert.equal(state.inFlight, 1);
 
-        let kvs = new protos.cockroach.server.serverpb.SetUIDataRequest(JSON.parse(requestObj.body as string)).getKeyValues();
+        let kvs = protos.cockroach.server.serverpb.SetUIDataRequest.decode(requestObj.body as ArrayBuffer).getKeyValues();
 
         assert.equal(kvs.size, 2);
 
-        let deserialize = function(buff: ByteBuffer): any {
-          return JSON.parse(buff.readString(buff.limit));
+        let deserialize = function(buff: ByteBuffer): Object {
+          return JSON.parse(buff.readString(buff.limit - buff.offset));
         };
 
         assert.deepEqual(deserialize(kvs.get(uiKey1)), uiObj1);
@@ -137,7 +137,7 @@ describe("UIData reducer", function() {
 
         return {
           sendAsJson: false,
-          body: new protos.cockroach.server.serverpb.SetUIDataResponse().encodeJSON(),
+          body: new protos.cockroach.server.serverpb.SetUIDataResponse().toArrayBuffer(),
         };
       });
 
@@ -183,7 +183,7 @@ describe("UIData reducer", function() {
         assert.equal(state.inFlight, 1);
 
         let response = new protos.cockroach.server.serverpb.GetUIDataResponse();
-        let setValue = function(key: string, obj: any) {
+        let setValue = function(key: string, obj: Object) {
           let value = new protos.cockroach.server.serverpb.GetUIDataResponse.Value();
           value.setValue(ByteBuffer.fromUTF8(JSON.stringify(obj)));
           response.key_values.set(key, value);
@@ -193,7 +193,7 @@ describe("UIData reducer", function() {
 
         return {
           sendAsJson: false,
-          body: response.encodeJSON(),
+          body: response.toArrayBuffer(),
         };
       });
 
diff --git a/ui/next/app/redux/uiData.ts b/ui/next/app/redux/uiData.ts
index 1108e33b8cb9..73c8e993e994 100644
--- a/ui/next/app/redux/uiData.ts
+++ b/ui/next/app/redux/uiData.ts
@@ -72,7 +72,7 @@ export default function(state = new UIDataSet(), action: Action): UIDataSet {
 /**
  * setUIDataKey sets the value of the given UIData key.
  */
-export function setUIDataKey(key: string, value: any): PayloadAction<KeyValue> {
+export function setUIDataKey(key: string, value: Object): PayloadAction<KeyValue> {
   return {
     type: SET,
     payload: { key, value },
@@ -113,7 +113,7 @@ export function fetchCompleteUIData(): Action {
  */
 export interface KeyValue {
   key: string;
-  value: any;
+  value: Object;
 }
 
 /**
@@ -156,9 +156,9 @@ export function loadUIData(...keys: string[]) {
       response.getKeyValues().forEach((val, key) => {
         // Responses from the server return values as ByteBuffer objects, which
         // represent stringified JSON objects.
-        let decoded: any = null;
+        let decoded: Object;
         let bb = val.getValue();
-        let str = bb.readString(bb.limit);
+        let str = bb.readString(bb.limit - bb.offset);
         if (str) {
           decoded = JSON.parse(str);
         }
diff --git a/ui/next/app/util/api.spec.ts b/ui/next/app/util/api.spec.ts
index 0e6819288de7..e068554aa970 100644
--- a/ui/next/app/util/api.spec.ts
+++ b/ui/next/app/util/api.spec.ts
@@ -111,7 +111,7 @@ describe("rest api", function() {
           sendAsJson: false,
           body: new protos.cockroach.server.serverpb.DatabasesResponse({
             databases: ["system", "test"],
-          }).encodeJSON(),
+          }).toArrayBuffer(),
         };
       });
 
@@ -175,7 +175,7 @@ describe("rest api", function() {
               { user: "root", privileges: ["ALL"] },
               { user: "other", privileges: [] },
             ],
-          }).encodeJSON(),
+          }).toArrayBuffer(),
         };
       });
 
@@ -235,7 +235,7 @@ describe("rest api", function() {
         assert.isUndefined(requestObj.body);
         return {
           sendAsJson: false,
-          body: new protos.cockroach.server.serverpb.TableDetailsResponse().encodeJSON(),
+          body: new protos.cockroach.server.serverpb.TableDetailsResponse().toArrayBuffer(),
         };
       });
 
@@ -298,7 +298,7 @@ describe("rest api", function() {
             events: [
               { event_type: "test" },
             ],
-          }).encodeJSON(),
+          }).toArrayBuffer(),
         };
       });
 
@@ -331,7 +331,7 @@ describe("rest api", function() {
             events: [
               { event_type: "test" },
             ],
-          }).encodeJSON(),
+          }).toArrayBuffer(),
         };
       });
 
@@ -359,7 +359,7 @@ describe("rest api", function() {
         assert.isUndefined(requestObj.body);
         return {
           sendAsJson: false,
-          body: new protos.cockroach.server.serverpb.EventsResponse().encodeJSON(),
+          body: new protos.cockroach.server.serverpb.EventsResponse().toArrayBuffer(),
         };
       });
 
@@ -416,7 +416,7 @@ describe("rest api", function() {
         assert.isUndefined(requestObj.body);
         return {
           sendAsJson: false,
-          body: new protos.cockroach.server.serverpb.HealthResponse().encodeJSON(),
+          body: new protos.cockroach.server.serverpb.HealthResponse().toArrayBuffer(),
         };
       });
 
diff --git a/ui/next/app/util/api.ts b/ui/next/app/util/api.ts
index 3c9b8c17bd12..254761c67701 100644
--- a/ui/next/app/util/api.ts
+++ b/ui/next/app/util/api.ts
@@ -87,12 +87,18 @@ function Fetch<TRequestMessage extends {
   return timeout(fetch(url, {
     method: req ? "POST" : "GET",
     headers: {
-      "Accept": "application/json",
-      "Content-Type": "application/json",
+      "Accept": "application/x-protobuf",
+      "Content-Type": "application/x-protobuf",
     },
-    body: req ? req.encodeJSON() : undefined,
-  })).then((res) => res.json<TResponse>()).then((json) => new builder(json));
+    body: req ? req.toArrayBuffer() : undefined,
+  })).then((res) => {
+    if (!res.ok) {
+      throw Error(res.statusText);
+    }
+    return res.arrayBuffer().then((buffer) => builder.decode(buffer));
+  });
 }
+
 // propsToQueryString is a helper function that converts a set of object
 // properties to a query string
 // - keys with null or undefined values will be skipped
diff --git a/ui/next/config.js b/ui/next/config.js
index 4f20d90a0e8e..0de3a7735c38 100644
--- a/ui/next/config.js
+++ b/ui/next/config.js
@@ -89,7 +89,7 @@ System.config({
     "ts": "github:frankwallis/plugin-typescript@4.0.16",
     "ts-runtime": "npm:babel-runtime@6.9.2",
     "typescript": "npm:typescript@1.8.10",
-    "whatwg-fetch": "npm:whatwg-fetch@1.0.0",
+    "whatwg-fetch": "github:tamird/fetch@support-arraybuffer",
     "github:dcodeIO/bytebuffer.js@5.0.1": {
       "long": "github:dcodeIO/long.js@3.1.0"
     },
diff --git a/ui/next/package.json b/ui/next/package.json
index 216627ef0534..3ebe822beaf1 100644
--- a/ui/next/package.json
+++ b/ui/next/package.json
@@ -34,7 +34,7 @@
       "redux": "npm:redux@^3.4.0",
       "redux-thunk": "npm:redux-thunk@^2.0.1",
       "reselect": "npm:reselect@^2.5.1",
-      "whatwg-fetch": "npm:whatwg-fetch@^1.0.0"
+      "whatwg-fetch": "github:tamird/fetch@support-arraybuffer"
     },
     "devDependencies": {
       "chai": "npm:chai@^3.5.0",