diff --git a/packages/server/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap b/packages/server/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap
deleted file mode 100644
index 80f2188e692..00000000000
--- a/packages/server/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap
+++ /dev/null
@@ -1,147 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`viewBuilder Calculate and filter creates a view with the calculation statistics and filter schema 1`] = `
-{
-  "map": "function (doc) {
-      if ((doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && !(
-      doc["myField"] === undefined ||
-      doc["myField"] === null ||
-      doc["myField"] === "" ||
-      (Array.isArray(doc["myField"]) && doc["myField"].length === 0)
-  )) && (doc["age"] > 17)) {
-        emit(doc["_id"], doc["myField"]);
-      }
-    }",
-  "meta": {
-    "calculation": "stats",
-    "field": "myField",
-    "filters": [
-      {
-        "condition": "MT",
-        "key": "age",
-        "value": 17,
-      },
-    ],
-    "groupBy": undefined,
-    "schema": {
-      "avg": {
-        "type": "number",
-      },
-      "count": {
-        "type": "number",
-      },
-      "field": {
-        "type": "string",
-      },
-      "max": {
-        "type": "number",
-      },
-      "min": {
-        "type": "number",
-      },
-      "sum": {
-        "type": "number",
-      },
-      "sumsqr": {
-        "type": "number",
-      },
-    },
-    "tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
-  },
-  "reduce": "_stats",
-}
-`;
-
-exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
-{
-  "map": "function (doc) {
-      if ((doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && !(
-      doc["myField"] === undefined ||
-      doc["myField"] === null ||
-      doc["myField"] === "" ||
-      (Array.isArray(doc["myField"]) && doc["myField"].length === 0)
-  )) ) {
-        emit(doc["_id"], doc["myField"]);
-      }
-    }",
-  "meta": {
-    "calculation": "stats",
-    "field": "myField",
-    "filters": [],
-    "groupBy": undefined,
-    "schema": {
-      "avg": {
-        "type": "number",
-      },
-      "count": {
-        "type": "number",
-      },
-      "field": {
-        "type": "string",
-      },
-      "max": {
-        "type": "number",
-      },
-      "min": {
-        "type": "number",
-      },
-      "sum": {
-        "type": "number",
-      },
-      "sumsqr": {
-        "type": "number",
-      },
-    },
-    "tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
-  },
-  "reduce": "_stats",
-}
-`;
-
-exports[`viewBuilder Filter creates a view with multiple filters and conjunctions 1`] = `
-{
-  "map": "function (doc) {
-      if (doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && (doc["Name"] === "Test" || doc["Yes"] > "Value")) {
-        emit(doc["_id"], doc["undefined"]);
-      }
-    }",
-  "meta": {
-    "calculation": undefined,
-    "field": undefined,
-    "filters": [
-      {
-        "condition": "EQUALS",
-        "key": "Name",
-        "value": "Test",
-      },
-      {
-        "condition": "MT",
-        "conjunction": "OR",
-        "key": "Yes",
-        "value": "Value",
-      },
-    ],
-    "groupBy": undefined,
-    "schema": null,
-    "tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
-  },
-}
-`;
-
-exports[`viewBuilder Group By creates a view emitting the group by field 1`] = `
-{
-  "map": "function (doc) {
-      if (doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" ) {
-        emit(doc["age"], doc["score"]);
-      }
-    }",
-  "meta": {
-    "calculation": undefined,
-    "field": "score",
-    "filters": [],
-    "groupBy": "age",
-    "schema": null,
-    "tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
-  },
-}
-`;
diff --git a/packages/server/src/api/controllers/view/tests/viewBuilder.spec.js b/packages/server/src/api/controllers/view/tests/viewBuilder.spec.js
deleted file mode 100644
index 7f4298b2ff1..00000000000
--- a/packages/server/src/api/controllers/view/tests/viewBuilder.spec.js
+++ /dev/null
@@ -1,75 +0,0 @@
-const viewTemplate = require("../viewBuilder").default
-
-describe("viewBuilder", () => {
-  describe("Filter", () => {
-    it("creates a view with multiple filters and conjunctions", () => {
-      expect(
-        viewTemplate({
-          name: "Test View",
-          tableId: "14f1c4e94d6a47b682ce89d35d4c78b0",
-          filters: [
-            {
-              value: "Test",
-              condition: "EQUALS",
-              key: "Name",
-            },
-            {
-              value: "Value",
-              condition: "MT",
-              key: "Yes",
-              conjunction: "OR",
-            },
-          ],
-        })
-      ).toMatchSnapshot()
-    })
-  })
-
-  describe("Calculate", () => {
-    it("creates a view with the calculation statistics schema", () => {
-      expect(
-        viewTemplate({
-          name: "Calculate View",
-          field: "myField",
-          calculation: "stats",
-          tableId: "14f1c4e94d6a47b682ce89d35d4c78b0",
-          filters: [],
-        })
-      ).toMatchSnapshot()
-    })
-  })
-
-  describe("Group By", () => {
-    it("creates a view emitting the group by field", () => {
-      expect(
-        viewTemplate({
-          name: "Test Scores Grouped By Age",
-          tableId: "14f1c4e94d6a47b682ce89d35d4c78b0",
-          groupBy: "age",
-          field: "score",
-          filters: [],
-        })
-      ).toMatchSnapshot()
-    })
-  })
-
-  describe("Calculate and filter", () => {
-    it("creates a view with the calculation statistics and filter schema", () => {
-      expect(
-        viewTemplate({
-          name: "Calculate View",
-          field: "myField",
-          calculation: "stats",
-          tableId: "14f1c4e94d6a47b682ce89d35d4c78b0",
-          filters: [
-            {
-              value: 17,
-              condition: "MT",
-              key: "age",
-            },
-          ],
-        })
-      ).toMatchSnapshot()
-    })
-  })
-})
diff --git a/packages/server/src/api/controllers/view/tests/viewBuilder.spec.ts b/packages/server/src/api/controllers/view/tests/viewBuilder.spec.ts
new file mode 100644
index 00000000000..34526345f33
--- /dev/null
+++ b/packages/server/src/api/controllers/view/tests/viewBuilder.spec.ts
@@ -0,0 +1,174 @@
+import viewTemplate from "../viewBuilder"
+
+describe("viewBuilder", () => {
+  describe("Filter", () => {
+    it("creates a view with multiple filters and conjunctions", () => {
+      expect(
+        viewTemplate({
+          field: "myField",
+          tableId: "tableId",
+          filters: [
+            {
+              value: "Test",
+              condition: "EQUALS",
+              key: "Name",
+            },
+            {
+              value: "Value",
+              condition: "MT",
+              key: "Yes",
+              conjunction: "OR",
+            },
+          ],
+        })
+      ).toEqual({
+        map: `function (doc) {
+      if (doc.tableId === "tableId" && (doc["Name"] === "Test" || doc["Yes"] > "Value")) {
+        emit(doc["_id"], doc["myField"]);
+      }
+    }`,
+        meta: {
+          calculation: undefined,
+          field: "myField",
+          filters: [
+            {
+              condition: "EQUALS",
+              key: "Name",
+              value: "Test",
+            },
+            {
+              condition: "MT",
+              conjunction: "OR",
+              key: "Yes",
+              value: "Value",
+            },
+          ],
+          groupBy: undefined,
+          schema: null,
+          tableId: "tableId",
+        },
+      })
+    })
+  })
+
+  describe("Calculate", () => {
+    it("creates a view with the calculation statistics schema", () => {
+      expect(
+        viewTemplate({
+          field: "myField",
+          calculation: "stats",
+          tableId: "tableId",
+          filters: [],
+        })
+      ).toEqual({
+        map: `function (doc) {
+      if ((doc.tableId === "tableId" && !(
+      doc["myField"] === undefined ||
+      doc["myField"] === null ||
+      doc["myField"] === "" ||
+      (Array.isArray(doc["myField"]) && doc["myField"].length === 0)
+  )) ) {
+        emit(doc["_id"], doc["myField"]);
+      }
+    }`,
+        meta: {
+          calculation: "stats",
+          field: "myField",
+          filters: [],
+          groupBy: undefined,
+          schema: {
+            min: { type: "number" },
+            max: { type: "number" },
+            avg: { type: "number" },
+            count: { type: "number" },
+            sumsqr: { type: "number" },
+            sum: { type: "number" },
+            field: { type: "string" },
+          },
+          tableId: "tableId",
+        },
+        reduce: "_stats",
+      })
+    })
+  })
+
+  describe("Group By", () => {
+    it("creates a view emitting the group by field", () => {
+      expect(
+        viewTemplate({
+          tableId: "tableId",
+          groupBy: "age",
+          field: "score",
+          filters: [],
+        })
+      ).toEqual({
+        map: `function (doc) {
+      if (doc.tableId === "tableId" ) {
+        emit(doc["age"], doc["score"]);
+      }
+    }`,
+        meta: {
+          calculation: undefined,
+          field: "score",
+          filters: [],
+          groupBy: "age",
+          schema: null,
+          tableId: "tableId",
+        },
+      })
+    })
+  })
+
+  describe("Calculate and filter", () => {
+    it("creates a view with the calculation statistics and filter schema", () => {
+      expect(
+        viewTemplate({
+          field: "myField",
+          calculation: "stats",
+          tableId: "tableId",
+          filters: [
+            {
+              value: 17,
+              condition: "MT",
+              key: "age",
+            },
+          ],
+        })
+      ).toEqual({
+        map: `function (doc) {
+      if ((doc.tableId === "tableId" && !(
+      doc["myField"] === undefined ||
+      doc["myField"] === null ||
+      doc["myField"] === "" ||
+      (Array.isArray(doc["myField"]) && doc["myField"].length === 0)
+  )) && (doc["age"] > 17)) {
+        emit(doc["_id"], doc["myField"]);
+      }
+    }`,
+        meta: {
+          calculation: "stats",
+          field: "myField",
+          filters: [
+            {
+              condition: "MT",
+              key: "age",
+              value: 17,
+            },
+          ],
+          groupBy: undefined,
+          schema: {
+            min: { type: "number" },
+            max: { type: "number" },
+            avg: { type: "number" },
+            count: { type: "number" },
+            sumsqr: { type: "number" },
+            sum: { type: "number" },
+            field: { type: "string" },
+          },
+          tableId: "tableId",
+        },
+        reduce: "_stats",
+      })
+    })
+  })
+})
diff --git a/packages/server/src/api/controllers/view/viewBuilder.ts b/packages/server/src/api/controllers/view/viewBuilder.ts
index 3df9df66573..2cedd1fc403 100644
--- a/packages/server/src/api/controllers/view/viewBuilder.ts
+++ b/packages/server/src/api/controllers/view/viewBuilder.ts
@@ -1,4 +1,4 @@
-import { ViewFilter, ViewTemplateOpts, DBView } from "@budibase/types"
+import { ViewFilter, DBView } from "@budibase/types"
 
 const TOKEN_MAP: Record<string, string> = {
   EQUALS: "===",
@@ -120,7 +120,7 @@ function parseFilterExpression(filters: ViewFilter[]) {
  * @param groupBy - field to group calculation results on, if any
  */
 function parseEmitExpression(field: string, groupBy: string) {
-  return `emit(doc["${groupBy || "_id"}"], doc["${field}"]);`
+  return `emit(doc["${groupBy}"], doc["${field}"]);`
 }
 
 /**
@@ -135,7 +135,19 @@ function parseEmitExpression(field: string, groupBy: string) {
  * calculation: an optional calculation to be performed over the view data.
  */
 export default function (
-  { field, tableId, groupBy, filters = [], calculation }: ViewTemplateOpts,
+  {
+    field,
+    tableId,
+    groupBy,
+    filters = [],
+    calculation,
+  }: {
+    field: string
+    tableId: string
+    groupBy?: string
+    filters?: ViewFilter[]
+    calculation?: string
+  },
   groupByMulti?: boolean
 ): DBView {
   // first filter can't have a conjunction
@@ -168,7 +180,7 @@ export default function (
   const parsedFilters = parseFilterExpression(filters)
   const filterExpression = parsedFilters ? `&& (${parsedFilters})` : ""
 
-  const emitExpression = parseEmitExpression(field, groupBy)
+  const emitExpression = parseEmitExpression(field, groupBy || "_id")
   const tableExpression = `doc.tableId === "${tableId}"`
   const coreExpression = statFilter
     ? `(${tableExpression} && ${statFilter})`
diff --git a/packages/types/src/documents/app/view.ts b/packages/types/src/documents/app/view.ts
index fd97cee4099..a7fbf063772 100644
--- a/packages/types/src/documents/app/view.ts
+++ b/packages/types/src/documents/app/view.ts
@@ -6,10 +6,10 @@ import { DBView, SearchFilters } from "../../sdk"
 export type ViewTemplateOpts = {
   field: string
   tableId: string
-  groupBy: string
+  groupBy?: string
   filters: ViewFilter[]
   schema: any
-  calculation: string
+  calculation?: string
   groupByMulti?: boolean
 }