diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx
index 2645a3d610cac7a8f7d42b3ab6151b89104b4193..be7ff82ee187367d1b3ab9d6f4ab98f4cb339040 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx
@@ -16,10 +16,7 @@ interface CacheSectionProps {
   onSave: (cache_ttl: number | null) => Promise<any>;
 }
 
-export const CacheSection = ({
-  initialCacheTTL,
-  onSave,
-}: CacheSectionProps) => {
+const CacheSection = ({ initialCacheTTL, onSave }: CacheSectionProps) => {
   const [cacheTTL, setCacheTTL] = useState(initialCacheTTL);
 
   const handleChange = useCallback(
@@ -70,3 +67,5 @@ export const CacheSection = ({
     </CacheSectionRoot>
   );
 };
+
+export default CacheSection;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js
deleted file mode 100644
index 945d4ff0f4a94ba9a3465f831687189844024d47..0000000000000000000000000000000000000000
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./CacheSection";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e2d399aae7c821bdd366a219426634dedf95f577
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts
@@ -0,0 +1 @@
+export { default } from "./CacheSection";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx
index 1c00d2a11c9a2b9b1b12559055ef68d988090b3c..22ea7f861b0c48877fce916b08105c6ec69a8662 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx
@@ -17,7 +17,7 @@ const propTypes = {
   message: PropTypes.string,
 };
 
-export function CacheTTLField({ field, message, ...props }) {
+function CacheTTLField({ field, message, ...props }) {
   const hasError = !!field.error;
   return (
     <CacheTTLFieldContainer {...props} data-testid="cache-ttl-field">
@@ -40,3 +40,5 @@ export function CacheTTLField({ field, message, ...props }) {
 }
 
 CacheTTLField.propTypes = propTypes;
+
+export default CacheTTLField;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js
index 1c2bc1a09ac54b2eefa92f67d7f9bbc338901786..206f0b1eb5492d09a347038e3a2ce86f0954e0ec 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js
@@ -1,7 +1,7 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
-import { CacheTTLField } from "./CacheTTLField";
+import CacheTTLField from "./CacheTTLField";
 
 function setup({ name = "cache_ttl", message, value }) {
   const onChange = jest.fn();
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js
index 35634c0a52f23cce0c369f18f465b2e0d83d75f4..fdb4536d31ceb8129f1fd63570031137dcd82560 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js
@@ -1 +1 @@
-export * from "./CacheTTLField";
+export { default } from "./CacheTTLField";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx
index 70c1606ae5b54411d4ba36f86c920d62932c153e..07db2a6f5b8e50667f9e89f862b942452a93d983 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx
@@ -1,15 +1,17 @@
 import React from "react";
 import { Dashboard } from "metabase-types/api";
-import { CacheSection } from "../CacheSection";
+import CacheSection from "../CacheSection";
 
 interface DashboardCacheSectionProps {
   dashboard: Dashboard;
   onSave: (cache_ttl: number | null) => Promise<Dashboard>;
 }
 
-export const DashboardCacheSection = ({
+const DashboardCacheSection = ({
   dashboard,
   onSave,
 }: DashboardCacheSectionProps) => {
   return <CacheSection initialCacheTTL={dashboard.cache_ttl} onSave={onSave} />;
 };
+
+export default DashboardCacheSection;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts
index 19cf2e99322536ea8a5d1a92ffbd47552d23463a..856e7a25f65a8962c41e023ce011e207a417866d 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts
@@ -1 +1 @@
-export * from "./DashboardCacheSection";
+export { default } from "./DashboardCacheSection";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx
index ae1574f47f4c3925c024e4bb60ba59f83fc7e005..883f1435f737c938865b6f2d30c64797eebacefb 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from "react";
 import PropTypes from "prop-types";
 import { t } from "ttag";
 import Select, { Option } from "metabase/core/components/Select";
-import { CacheTTLField } from "../CacheTTLField";
+import CacheTTLField from "../CacheTTLField";
 import {
   CacheFieldContainer,
   FieldContainer,
@@ -20,7 +20,7 @@ const propTypes = {
   field: PropTypes.object.isRequired,
 };
 
-export function DatabaseCacheTTLField({ field }) {
+function DatabaseCacheTTLField({ field }) {
   const [mode, setMode] = useState(
     field.value > 0 ? MODE.CUSTOM : MODE.INSTANCE_DEFAULT,
   );
@@ -55,3 +55,5 @@ export function DatabaseCacheTTLField({ field }) {
 }
 
 DatabaseCacheTTLField.propTypes = propTypes;
+
+export default DatabaseCacheTTLField;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js
index e4670251a82814b578606c5a61490f88e9c954ef..11a4debeb19af3c9c833fb210d431b4f4f8a9b2e 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js
@@ -1,7 +1,7 @@
 import React from "react";
 import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
-import { DatabaseCacheTTLField } from "./DatabaseCacheTTLField";
+import DatabaseCacheTTLField from "./DatabaseCacheTTLField";
 
 function setup({ value = null } = {}) {
   const onChange = jest.fn();
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js
index 0e1d95230ac8c9561887e9cf1bd533b82502a442..eedffdc43ac4ffe356f2dddf31c1a3328531e83d 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js
@@ -1 +1 @@
-export * from "./DatabaseCacheTTLField";
+export { default } from "./DatabaseCacheTTLField";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..33380eb47335005faa6cb9e437cc8411e490c8c3
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx
@@ -0,0 +1,8 @@
+import styled from "@emotion/styled";
+import { color } from "metabase/lib/colors";
+
+export const QueryStartLabel = styled.div`
+  color: ${color("text-dark")};
+  font-weight: bold;
+  margin-bottom: 0.5rem;
+`;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx
index 14651709222afbb316f7c5a0bd27707999443e46..e76a23dde785a2c1752cd21be322de3844f5df28 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx
@@ -1,15 +1,32 @@
 import React from "react";
+import { t } from "ttag";
+import { getRelativeTime } from "metabase/lib/time";
 import Question from "metabase-lib/lib/Question";
-import { CacheSection } from "../CacheSection";
+import CacheSection from "../CacheSection";
+import { QueryStartLabel } from "./QuestionCacheSection.styled";
 
-interface QuestionCacheSectionProps {
+export interface QuestionCacheSectionProps {
   question: Question;
   onSave: (cache_ttl: number | null) => Promise<Question>;
 }
 
-export const QuestionCacheSection = ({
+const QuestionCacheSection = ({
   question,
   onSave,
 }: QuestionCacheSectionProps) => {
-  return <CacheSection initialCacheTTL={question.cacheTTL()} onSave={onSave} />;
+  const cacheTimestamp = question.lastQueryStart();
+  const cacheRelativeTime = cacheTimestamp && getRelativeTime(cacheTimestamp);
+
+  return (
+    <div>
+      {cacheTimestamp && (
+        <QueryStartLabel>
+          {t`Question last cached ${cacheRelativeTime}`}
+        </QueryStartLabel>
+      )}
+      <CacheSection initialCacheTTL={question.cacheTTL()} onSave={onSave} />
+    </div>
+  );
 };
+
+export default QuestionCacheSection;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..e49bc029b83302aea48b5430e466b58231046256
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx
@@ -0,0 +1,41 @@
+import React from "react";
+import { render, screen } from "@testing-library/react";
+import Question from "metabase-lib/lib/Question";
+import { createMockCard } from "metabase-types/api/mocks";
+import QuestionCacheSection, {
+  QuestionCacheSectionProps,
+} from "./QuestionCacheSection";
+
+describe("QuestionCacheSection", () => {
+  beforeEach(() => {
+    jest.useFakeTimers();
+    jest.setSystemTime(new Date(2020, 0, 10));
+  });
+
+  afterEach(() => {
+    jest.useRealTimers();
+  });
+
+  it("should show the time of the last cached query", () => {
+    const props = getProps({
+      question: new Question(
+        createMockCard({
+          last_query_start: "2020-01-05T00:00:00Z",
+        }),
+      ),
+    });
+
+    render(<QuestionCacheSection {...props} />);
+
+    const cacheLabel = screen.getByText("Question last cached 5 days ago");
+    expect(cacheLabel).toBeInTheDocument();
+  });
+});
+
+const getProps = (
+  opts?: Partial<QuestionCacheSectionProps>,
+): QuestionCacheSectionProps => ({
+  question: new Question(createMockCard()),
+  onSave: jest.fn(),
+  ...opts,
+});
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js
deleted file mode 100644
index 4a9bc9fc8c5ecfafa8c0b432e8dca6e4ff165b78..0000000000000000000000000000000000000000
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export * from "./QuestionCacheSection";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..15d8fcdef47aa0bbd7182c890e64c29768cfdbf5
--- /dev/null
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts
@@ -0,0 +1 @@
+export { default } from "./QuestionCacheSection";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx
index 51ab91308c9270eb24ff50e4b0e4376012ffa598..65843b980eb57d34ca2e47830cfcc537aabb83ac 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx
@@ -31,7 +31,7 @@ function getInitialMode(question, implicitCacheTTL) {
   return MODE.DEFAULT;
 }
 
-export function QuestionCacheTTLField({ field, question, ...props }) {
+function QuestionCacheTTLField({ field, question, ...props }) {
   const implicitCacheTTL = useMemo(
     () => getQuestionsImplicitCacheTTL(question),
     [question],
@@ -73,3 +73,5 @@ export function QuestionCacheTTLField({ field, question, ...props }) {
 }
 
 QuestionCacheTTLField.propTypes = propTypes;
+
+export default QuestionCacheTTLField;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx
index b10280682c6d4479988ebfcdfba83ceaecd7d653..d45fe16ef15ffaf440e3064cf53e658ff84a7825 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx
@@ -3,7 +3,7 @@ import { t } from "ttag";
 import styled from "@emotion/styled";
 import { space } from "metabase/styled-components/theme";
 import Radio from "metabase/core/components/Radio";
-import { CacheTTLField } from "../CacheTTLField";
+import CacheTTLField from "../CacheTTLField";
 
 export function CacheTTLInput(props) {
   return <CacheTTLField {...props} message={t`Cache results for`} />;
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js
index c893c54c12890266d052998f0e21dbdc3c8c7cd3..4ef6867209dda455b0d5f74c7c36307b1681dc29 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js
@@ -3,7 +3,7 @@ import { render, screen } from "@testing-library/react";
 import userEvent from "@testing-library/user-event";
 import { msToMinutes, msToHours } from "metabase/lib/time";
 import MetabaseSettings from "metabase/lib/settings";
-import { QuestionCacheTTLField } from "./QuestionCacheTTLField";
+import QuestionCacheTTLField from "./QuestionCacheTTLField";
 
 const TEN_MINUTES = 10 * 60 * 1000;
 
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js
index bbc522fae5247d0b8615ebdbd2a78005e6403221..ce8a3adf3bb9ec5c81527b65d175da9c105c0171 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js
@@ -1 +1 @@
-export * from "./QuestionCacheTTLField";
+export { default } from "./QuestionCacheTTLField";
diff --git a/enterprise/frontend/src/metabase-enterprise/caching/index.js b/enterprise/frontend/src/metabase-enterprise/caching/index.js
index d3b4b61fedffc84c9d27c9bd6c9727114f2923fd..a1c91a17b244bdb4a90fc74d27d740e1d267e588 100644
--- a/enterprise/frontend/src/metabase-enterprise/caching/index.js
+++ b/enterprise/frontend/src/metabase-enterprise/caching/index.js
@@ -3,11 +3,11 @@ import { t, jt } from "ttag";
 import { hasPremiumFeature } from "metabase-enterprise/settings";
 import { PLUGIN_CACHING, PLUGIN_FORM_WIDGETS } from "metabase/plugins";
 import Link from "metabase/core/components/Link";
-import { CacheTTLField } from "./components/CacheTTLField";
-import { DatabaseCacheTTLField } from "./components/DatabaseCacheTTLField";
-import { QuestionCacheTTLField } from "./components/QuestionCacheTTLField";
-import { QuestionCacheSection } from "./components/QuestionCacheSection";
-import { DashboardCacheSection } from "./components/DashboardCacheSection";
+import CacheTTLField from "./components/CacheTTLField";
+import DatabaseCacheTTLField from "./components/DatabaseCacheTTLField";
+import QuestionCacheTTLField from "./components/QuestionCacheTTLField";
+import QuestionCacheSection from "./components/QuestionCacheSection";
+import DashboardCacheSection from "./components/DashboardCacheSection";
 
 import {
   getQuestionsImplicitCacheTTL,
diff --git a/frontend/src/metabase-lib/lib/Question.ts b/frontend/src/metabase-lib/lib/Question.ts
index 6b2b0a9efd75ee7f5aae190531b0b4f46999e30f..19390191428771f672f7108eadab1667cfe72dbb 100644
--- a/frontend/src/metabase-lib/lib/Question.ts
+++ b/frontend/src/metabase-lib/lib/Question.ts
@@ -879,6 +879,10 @@ class QuestionInner {
     return this._card && this._card["last-edit-info"];
   }
 
+  lastQueryStart() {
+    return this._card?.last_query_start;
+  }
+
   isSaved(): boolean {
     return !!this.id();
   }
diff --git a/frontend/src/metabase-types/api/card.ts b/frontend/src/metabase-types/api/card.ts
index 1b910ae11a2b7da9ca7841e7f27ed37129c59ca9..250d77fab289de954e12e635484db6a25fa1c6e6 100644
--- a/frontend/src/metabase-types/api/card.ts
+++ b/frontend/src/metabase-types/api/card.ts
@@ -1,3 +1,21 @@
+import { DatasetQuery } from "./query";
+
+export interface Card extends UnsavedCard {
+  id: CardId;
+  name: string;
+  description: string | null;
+  dataset: boolean;
+  can_write: boolean;
+  cache_ttl: number | null;
+  last_query_start: string | null;
+}
+
+export interface UnsavedCard {
+  display: string;
+  dataset_query: DatasetQuery;
+  visualization_settings: VisualizationSettings;
+}
+
 export type VisualizationSettings = {
   [key: string]: any;
 };
@@ -9,4 +27,5 @@ export interface ModerationReview {
   most_recent: boolean;
 }
 
+export type CardId = number;
 export type ModerationReviewStatus = "verified";
diff --git a/frontend/src/metabase-types/api/foreignKey.ts b/frontend/src/metabase-types/api/foreign-key.ts
similarity index 100%
rename from frontend/src/metabase-types/api/foreignKey.ts
rename to frontend/src/metabase-types/api/foreign-key.ts
diff --git a/frontend/src/metabase-types/api/index.ts b/frontend/src/metabase-types/api/index.ts
index 8364d41e44fba52d05eab17618708a16e699c899..32fe209bd1031033a69f8b2d31fc55e9214cc21b 100644
--- a/frontend/src/metabase-types/api/index.ts
+++ b/frontend/src/metabase-types/api/index.ts
@@ -5,16 +5,17 @@ export * from "./card";
 export * from "./collection";
 export * from "./dashboard";
 export * from "./database";
-export * from "./table";
+export * from "./dataset";
 export * from "./field";
-export * from "./timeline";
-export * from "./settings";
-export * from "./slack";
-export * from "./user";
+export * from "./foreign-key";
 export * from "./group";
-export * from "./permissions";
-export * from "./question";
-export * from "./dataset";
 export * from "./models";
 export * from "./notifications";
+export * from "./permissions";
+export * from "./query";
 export * from "./revision";
+export * from "./settings";
+export * from "./slack";
+export * from "./table";
+export * from "./timeline";
+export * from "./user";
diff --git a/frontend/src/metabase-types/api/mocks/card.ts b/frontend/src/metabase-types/api/mocks/card.ts
index deed6e5220a491341ceb00681dc115354d20236c..41bdb3a80e3f3fd39e15e092f87f19af63033853 100644
--- a/frontend/src/metabase-types/api/mocks/card.ts
+++ b/frontend/src/metabase-types/api/mocks/card.ts
@@ -1,4 +1,39 @@
-import { ModerationReview } from "metabase-types/api";
+import {
+  ModerationReview,
+  Card,
+  UnsavedCard,
+  VisualizationSettings,
+} from "metabase-types/api";
+import { createMockStructuredDatasetQuery } from "./query";
+
+export const createMockCard = (opts?: Partial<Card>): Card => ({
+  id: 1,
+  name: "Question",
+  description: null,
+  display: "table",
+  dataset_query: createMockStructuredDatasetQuery(),
+  visualization_settings: createMockVisualizationSettings(),
+  dataset: false,
+  can_write: false,
+  cache_ttl: null,
+  last_query_start: null,
+  ...opts,
+});
+
+export const createMockUnsavedCard = (
+  opts?: Partial<UnsavedCard>,
+): UnsavedCard => ({
+  display: "table",
+  dataset_query: createMockStructuredDatasetQuery(),
+  visualization_settings: createMockVisualizationSettings(),
+  ...opts,
+});
+
+export const createMockVisualizationSettings = (
+  opts?: Partial<VisualizationSettings>,
+): VisualizationSettings => ({
+  ...opts,
+});
 
 export const createMockModerationReview = (
   opts?: Partial<ModerationReview>,
diff --git a/frontend/src/metabase-types/api/mocks/index.ts b/frontend/src/metabase-types/api/mocks/index.ts
index da5f4b52d9244b9336c39a7992b2458d4ede5019..8aabfb3f245cd5c5f6cc306ab110ae5d580dc271 100644
--- a/frontend/src/metabase-types/api/mocks/index.ts
+++ b/frontend/src/metabase-types/api/mocks/index.ts
@@ -6,6 +6,7 @@ export * from "./dashboard";
 export * from "./database";
 export * from "./dataset";
 export * from "./models";
+export * from "./query";
 export * from "./table";
 export * from "./timeline";
 export * from "./settings";
diff --git a/frontend/src/metabase-types/api/mocks/query.ts b/frontend/src/metabase-types/api/mocks/query.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ae85cf9d09ba305b4fc0169ceea0984ed685bb8
--- /dev/null
+++ b/frontend/src/metabase-types/api/mocks/query.ts
@@ -0,0 +1,37 @@
+import {
+  NativeDatasetQuery,
+  NativeQuery,
+  StructuredDatasetQuery,
+  StructuredQuery,
+} from "metabase-types/api";
+
+export const createMockStructuredQuery = (
+  opts?: Partial<StructuredQuery>,
+): StructuredQuery => ({
+  ...opts,
+});
+
+export const createMockNativeQuery = (
+  opts?: Partial<NativeQuery>,
+): NativeQuery => ({
+  query: "SELECT 1",
+  ...opts,
+});
+
+export const createMockStructuredDatasetQuery = (
+  opts?: Partial<StructuredDatasetQuery>,
+): StructuredDatasetQuery => ({
+  type: "query",
+  database: 1,
+  query: createMockStructuredQuery(),
+  ...opts,
+});
+
+export const createMockNativeDatasetQuery = (
+  opts?: Partial<NativeDatasetQuery>,
+): NativeDatasetQuery => ({
+  type: "native",
+  database: 1,
+  query: createMockNativeQuery(),
+  ...opts,
+});
diff --git a/frontend/src/metabase-types/api/query.ts b/frontend/src/metabase-types/api/query.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a8a490bee161390b4d7948fd6e21c020fbce0f7c
--- /dev/null
+++ b/frontend/src/metabase-types/api/query.ts
@@ -0,0 +1,24 @@
+import { DatabaseId } from "./database";
+import { TableId } from "./table";
+
+export interface StructuredQuery {
+  "source-table"?: TableId;
+}
+
+export interface NativeQuery {
+  query: string;
+}
+
+export interface StructuredDatasetQuery {
+  type: "query";
+  database: DatabaseId;
+  query: StructuredQuery;
+}
+
+export interface NativeDatasetQuery {
+  type: "native";
+  database: DatabaseId;
+  query: NativeQuery;
+}
+
+export type DatasetQuery = StructuredDatasetQuery | NativeDatasetQuery;
diff --git a/frontend/src/metabase-types/api/question.ts b/frontend/src/metabase-types/api/question.ts
deleted file mode 100644
index 464b6cddaa5616fa4b50238f575e8b06ffa7af61..0000000000000000000000000000000000000000
--- a/frontend/src/metabase-types/api/question.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type CardId = number;
diff --git a/frontend/src/metabase-types/api/table.ts b/frontend/src/metabase-types/api/table.ts
index 003007194f91f56e371d20d1683733d609682eac..91e63785d68f8d3d748fb19e7a82ee0f148f04ab 100644
--- a/frontend/src/metabase-types/api/table.ts
+++ b/frontend/src/metabase-types/api/table.ts
@@ -1,7 +1,9 @@
-import { ForeignKey } from "../api/foreignKey";
+import { ForeignKey } from "./foreign-key";
 import { Database } from "./database";
 import { Field } from "./field";
 
+export type TableId = number | string; // can be string for virtual questions (e.g. "card__17")
+
 export type VisibilityType =
   | null
   | "details-only"
@@ -13,7 +15,7 @@ export type VisibilityType =
   | "cruft";
 
 export interface Table {
-  id: number | string; // can be string for virtual questions (e.g. "card__17")
+  id: TableId;
   db_id: number;
   db?: Database;
   name: string;
diff --git a/frontend/src/metabase-types/types/Table.ts b/frontend/src/metabase-types/types/Table.ts
index 4597c4ffbbcb544bd89b9a40a75b8c2704c83231..6a63bca33791ac533bfaf8d82052c2dc8ff451d7 100644
--- a/frontend/src/metabase-types/types/Table.ts
+++ b/frontend/src/metabase-types/types/Table.ts
@@ -4,7 +4,7 @@ import { Field } from "./Field";
 import { Segment } from "./Segment";
 import { Metric } from "./Metric";
 import { DatabaseId } from "./Database";
-import { ForeignKey } from "../api/foreignKey";
+import { ForeignKey } from "../api/foreign-key";
 
 export type TableId = number;
 export type SchemaName = string;
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
index d68c5f60c9179006c1b61ede0abd496fd51c2674..8202d0ec56f4476ac8cf5d916af5ef6b584d96b1 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx
@@ -6,7 +6,7 @@ import Question from "metabase-lib/lib/Question";
 import { isPK } from "metabase/lib/schema_metadata";
 import { Table } from "metabase-types/types/Table";
 
-import { ForeignKey } from "metabase-types/api/foreignKey";
+import { ForeignKey } from "metabase-types/api";
 import { DatasetData } from "metabase-types/types/Dataset";
 import { ObjectId, OnVisualizationClickType } from "./types";
 
diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx
index 1a6c20c4db71e9f554884a2842d8fd0a3216498f..3aca8c441441f2b491289fd8c4cdbeddf2b37ae9 100644
--- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx
+++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx
@@ -3,7 +3,7 @@ import { t, jt } from "ttag";
 import cx from "classnames";
 import { inflect } from "inflection";
 
-import { ForeignKey } from "metabase-types/api/foreignKey";
+import { ForeignKey } from "metabase-types/api";
 
 import IconBorder from "metabase/components/IconBorder";
 import LoadingSpinner from "metabase/components/LoadingSpinner";