diff --git a/prisma/migrations/20220423191830_drop_stars_column/migration.sql b/prisma/migrations/20220423191830_drop_stars_column/migration.sql
new file mode 100644
index 0000000..8ad6b6d
--- /dev/null
+++ b/prisma/migrations/20220423191830_drop_stars_column/migration.sql
@@ -0,0 +1,12 @@
+/*
+  Warnings:
+
+  - You are about to drop the column `stars` on the `history` table. All the data in the column will be lost.
+  - You are about to drop the column `stars` on the `questions` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE "history" DROP COLUMN "stars";
+
+-- AlterTable
+ALTER TABLE "questions" DROP COLUMN "stars";
diff --git a/prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql b/prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql
new file mode 100644
index 0000000..4dee470
--- /dev/null
+++ b/prisma/migrations/20220423192234_drop_old_frontpage_table/migration.sql
@@ -0,0 +1,8 @@
+/*
+  Warnings:
+
+  - You are about to drop the `frontpage` table. If the table is not empty, all the data it contains will be lost.
+
+*/
+-- DropTable
+DROP TABLE "frontpage";
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 49baa57..9b5a035 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -23,14 +23,6 @@ model Dashboard {
   @@map("dashboards")
 }
 
-model Frontpage {
-  id               Int  @id @default(autoincrement())
-  frontpage_full   Json
-  frontpage_sliced Json
-
-  @@map("frontpage")
-}
-
 model History {
   id                String
   title             String
@@ -39,7 +31,6 @@ model History {
   description       String
   options           Json
   timestamp         DateTime @db.Timestamp(6)
-  stars             Int
   qualityindicators Json
   extra             Json
   pk                Int      @id @default(autoincrement())
@@ -49,14 +40,37 @@ model History {
 }
 
 model Question {
-  id                String   @id
-  title             String
-  url               String
-  platform          String
-  description       String
-  options           Json
-  timestamp         DateTime @db.Timestamp(6)
-  stars             Int
+  /// E.g. "fantasyscotus-580"
+  id          String @id
+  /// E.g. "In Wooden v. U.S., the SCOTUS will affirm the lower court's decision"
+  title       String
+  /// E.g. "https://fantasyscotus.net/user-predictions/case/wooden-v-us/"
+  url         String
+  /// E.g. "fantasyscotus"
+  platform    String
+  /// E.g. "62.50% (75 out of 120) of FantasySCOTUS players predict that the lower court's decision will be affirmed. FantasySCOTUS overall predicts an outcome of Affirm 6-3. Historically, FantasySCOTUS has chosen the correct side 50.00% of the time."
+  description String
+
+  // E.g.:
+  // [
+  //   {
+  //     "name": "Yes",
+  //     "probability": 0.625,
+  //     "type": "PROBABILITY"
+  //   },
+  //   {
+  //     "name": "No",
+  //     "probability": 0.375,
+  //     "type": "PROBABILITY"
+  //   }
+  // ]
+  options   Json
+  timestamp DateTime @db.Timestamp(6)
+
+  // {
+  //   "numforecasts": 120,
+  //   "stars": 2
+  // }
   qualityindicators Json
   extra             Json
 
diff --git a/src/backend/database/pg-wrapper.ts b/src/backend/database/pg-wrapper.ts
deleted file mode 100644
index f7bc03f..0000000
--- a/src/backend/database/pg-wrapper.ts
+++ /dev/null
@@ -1,163 +0,0 @@
-import { Pool, PoolClient } from "pg";
-
-import { Question } from "../platforms";
-import { measureTime } from "../utils/measureTime";
-import { roughSizeOfObject } from "../utils/roughSize";
-
-const questionTableNames = ["questions", "history"];
-
-const allTableNames = [...questionTableNames, "dashboards", "frontpage"];
-
-/* Postgres database connection code */
-const databaseURL = process.env.DIGITALOCEAN_POSTGRES;
-export const pool = new Pool({
-  connectionString: databaseURL,
-  ssl: process.env.POSTGRES_NO_SSL
-    ? false
-    : {
-        rejectUnauthorized: false,
-      },
-});
-
-// Read
-export async function pgRead({ tableName }: { tableName: string }) {
-  if (!allTableNames.includes(tableName)) {
-    throw Error(
-      `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections`
-    );
-  }
-  let command = `SELECT * from ${tableName}`;
-  return (await pool.query(command)).rows;
-}
-
-export async function pgBulkInsert({
-  data,
-  tableName,
-  client,
-}: {
-  data: Question[];
-  tableName: string;
-  client: PoolClient;
-}) {
-  if (!questionTableNames.includes(tableName)) {
-    throw Error(
-      `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections`
-    );
-  }
-
-  const generateQuery = (rows: number) => {
-    let text = `INSERT INTO ${tableName} VALUES`;
-    const cols = 10;
-    const parts: string[] = [];
-    for (let r = 0; r < rows; r++) {
-      const bits = [];
-      for (let c = 1; c <= cols; c++) {
-        bits.push(`$${cols * r + c}`);
-      }
-      parts.push("(" + bits.join(", ") + ")");
-    }
-
-    text += parts.join(", ");
-    return text;
-  };
-
-  let from = 0;
-  const chunkSize = 20;
-  while (from < data.length - 1) {
-    const take = Math.min(chunkSize, data.length - from);
-    const query = generateQuery(take);
-
-    const chunk = [];
-    for (let i = from; i < from + take; i++) {
-      const datum = data[i];
-      let timestamp =
-        datum.timestamp &&
-        !!datum.timestamp.slice &&
-        !isNaN(Date.parse(datum.timestamp))
-          ? datum.timestamp
-          : new Date().toISOString();
-      timestamp = timestamp.slice(0, 19).replace("T", " ");
-      const values = [
-        datum.id,
-        datum.title,
-        datum.url,
-        datum.platform,
-        datum.description || "",
-        JSON.stringify(datum.options || []),
-        timestamp, // fix
-        datum.stars ||
-          (datum.qualityindicators ? datum.qualityindicators.stars : 2),
-        JSON.stringify(datum.qualityindicators || []),
-        JSON.stringify(datum.extra || []),
-      ];
-      chunk.push(...values);
-    }
-
-    console.log(`Inserting ${from + 1}..${from + take}`);
-    from += take;
-    await client.query(query, chunk);
-  }
-}
-
-export async function pgUpsert({
-  contents,
-  tableName,
-  replacePlatform,
-}: {
-  contents: Question[];
-  tableName: string;
-  replacePlatform?: string;
-}) {
-  if (!questionTableNames.includes(tableName)) {
-    throw Error(
-      `Table ${tableName} not in whitelist; stopping to avoid tricky sql injections`
-    );
-  }
-
-  await measureTime(async () => {
-    const client = await pool.connect();
-    try {
-      await client.query("BEGIN");
-      if (replacePlatform) {
-        await client.query(`DELETE FROM ${tableName} WHERE platform = $1`, [
-          replacePlatform,
-        ]);
-      }
-      console.log(
-        `Upserting ${contents.length} rows into postgres table ${tableName}.`
-      );
-
-      await pgBulkInsert({ data: contents, tableName, client });
-      console.log(
-        `Inserted ${
-          contents.length
-        } rows with approximate cummulative size ${roughSizeOfObject(
-          contents
-        )} MB into ${tableName}.`
-      );
-
-      console.log("Sample: ");
-      console.log(
-        JSON.stringify(
-          // only show the first three options
-          contents.slice(0, 1).map((question) => ({
-            ...question,
-            options: question.options
-              ? question.options.length > 3
-                ? question.options.slice(0, 3).concat("...")
-                : question.options
-              : null,
-          })),
-          null,
-          4
-        )
-      );
-      await client.query("COMMIT");
-    } catch (e) {
-      await client.query("ROLLBACK");
-      throw e;
-    } finally {
-      client.release();
-    }
-  });
-}
diff --git a/src/backend/flow/history/updateHistory.ts b/src/backend/flow/history/updateHistory.ts
index d3e704e..88aea20 100644
--- a/src/backend/flow/history/updateHistory.ts
+++ b/src/backend/flow/history/updateHistory.ts
@@ -1,9 +1,8 @@
-import { pgRead, pgUpsert } from "../../database/pg-wrapper";
+import { prisma } from "../../database/prisma";
 
 export async function updateHistory() {
-  let latest = await pgRead({ tableName: "questions" });
-  await pgUpsert({
-    contents: latest,
-    tableName: "history",
+  const questions = await prisma.question.findMany({});
+  await prisma.history.createMany({
+    data: questions,
   });
 }
diff --git a/src/backend/manual/manualDownload.ts b/src/backend/manual/manualDownload.ts
index 88472ef..8550904 100644
--- a/src/backend/manual/manualDownload.ts
+++ b/src/backend/manual/manualDownload.ts
@@ -2,10 +2,10 @@ import "dotenv/config";
 
 import fs from "fs";
 
-import { pgRead } from "../database/pg-wrapper";
+import { prisma } from "../database/prisma";
 
 let main = async () => {
-  let json = await pgRead({ tableName: "questions" });
+  let json = await prisma.question.findMany({});
   let string = JSON.stringify(json, null, 2);
   let filename = "metaforecasts.json";
   fs.writeFileSync(filename, string);
diff --git a/src/backend/migrate/noSchemaMigrate.ts b/src/backend/migrate/noSchemaMigrate.ts
deleted file mode 100644
index 43bbf20..0000000
--- a/src/backend/migrate/noSchemaMigrate.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import "dotenv/config";
-
-import { pool } from "../database/pg-wrapper";
-
-const migrate = async () => {
-  const client = await pool.connect();
-
-  const execQuery = async (q: string) => {
-    console.log(q);
-    await client.query(q);
-  };
-
-  const platformTitleToName = {
-    Betfair: "betfair",
-    FantasySCOTUS: "fantasyscotus",
-    Foretold: "foretold",
-    "GiveWell/OpenPhilanthropy": "givewellopenphil",
-    "Good Judgment": "goodjudgment",
-    "Good Judgment Open": "goodjudgmentopen",
-    Infer: "infer",
-    Kalshi: "kalshi",
-    "Manifold Markets": "manifold",
-    Metaculus: "metaculus",
-    "Peter Wildeford": "wildeford",
-    PolyMarket: "polymarket",
-    PredictIt: "predictit",
-    Rootclaim: "rootclaim",
-    Smarkets: "smarkets",
-    "X-risk estimates": "xrisk",
-  };
-
-  try {
-    await client.query("BEGIN");
-    const copyTable = async (from: string, to: string) => {
-      await execQuery(`DROP TABLE IF EXISTS ${to}`);
-      await execQuery(`CREATE TABLE ${to} (LIKE ${from} INCLUDING ALL)`);
-      await execQuery(`INSERT INTO ${to} SELECT * FROM ${from}`);
-    };
-
-    await copyTable("latest.dashboards", "dashboards");
-    await copyTable("latest.combined", "questions");
-    await copyTable("latest.frontpage", "frontpage");
-    await copyTable("history.h2022", "history");
-
-    for (const [title, name] of Object.entries(platformTitleToName)) {
-      console.log(`Updating ${title} -> ${name}`);
-      for (const table of ["questions", "history"]) {
-        await client.query(
-          `UPDATE ${table} SET platform=$1 WHERE platform=$2`,
-          [name, title]
-        );
-      }
-    }
-
-    console.log("Fixing GJOpen ids in questions and history");
-    for (const table of ["questions", "history"]) {
-      await client.query(
-        `UPDATE ${table} SET id=REPLACE(id, 'goodjudmentopen-', 'goodjudgmentopen-') WHERE id LIKE 'goodjudmentopen-%'`
-      );
-    }
-
-    const fixId = (id: string) =>
-      id.replace("goodjudmentopen-", "goodjudgmentopen-");
-
-    console.log(
-      "Please rebuild frontpage manually - current version includes invalid GJOpen and xrisk ids"
-    );
-
-    const updateDashboards = async () => {
-      const res = await client.query("SELECT id, contents FROM dashboards");
-      for (const row of res.rows) {
-        let { id, contents } = row;
-        contents = contents.map(fixId);
-        await client.query(
-          "UPDATE dashboards SET contents = $1 WHERE id = $2",
-          [JSON.stringify(contents), id]
-        );
-      }
-    };
-    console.log("Updating dashboards");
-    await updateDashboards();
-
-    await client.query("COMMIT");
-  } catch (e) {
-    await client.query("ROLLBACK");
-    throw e;
-  } finally {
-    client.release();
-  }
-};
-
-migrate();
diff --git a/src/backend/migrate/prepareForPrisma.ts b/src/backend/migrate/prepareForPrisma.ts
deleted file mode 100644
index cee8905..0000000
--- a/src/backend/migrate/prepareForPrisma.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import "dotenv/config";
-
-import { pool } from "../database/pg-wrapper";
-
-const migrate = async () => {
-  const client = await pool.connect();
-
-  const execQuery = async (q: string) => {
-    console.log(q);
-    await client.query(q);
-  };
-
-  try {
-    await client.query("BEGIN");
-
-    const notNullColumn = async (table: string, column: string) => {
-      await execQuery(
-        `ALTER TABLE ${table} ALTER COLUMN ${column} SET NOT NULL`
-      );
-    };
-
-    const jsonbColumn = async (table: string, column: string) => {
-      await execQuery(
-        `ALTER TABLE ${table} ALTER COLUMN ${column} SET DATA TYPE jsonb USING ${column}::jsonb`
-      );
-    };
-
-    const t2c = {
-      dashboards: [
-        "id",
-        "title",
-        "description",
-        "contents",
-        "timestamp",
-        "creator",
-        "extra",
-      ],
-      frontpage: ["frontpage_sliced", "frontpage_full"],
-      history: [
-        "id",
-        "title",
-        "url",
-        "platform",
-        "description",
-        "options",
-        "timestamp",
-        "stars",
-        "qualityindicators",
-        "extra",
-      ],
-      questions: [
-        "id",
-        "title",
-        "url",
-        "platform",
-        "description",
-        "options",
-        "timestamp",
-        "stars",
-        "qualityindicators",
-        "extra",
-      ],
-    };
-    for (const [table, columns] of Object.entries(t2c)) {
-      for (const column of columns) {
-        await notNullColumn(table, column);
-      }
-    }
-
-    await execQuery("ALTER TABLE history ADD COLUMN pk SERIAL PRIMARY KEY");
-    await execQuery("ALTER TABLE dashboards ADD PRIMARY KEY (id)");
-    await execQuery("ALTER TABLE questions ADD PRIMARY KEY (id)");
-
-    await jsonbColumn("dashboards", "contents");
-    await jsonbColumn("dashboards", "extra");
-
-    for (const table of ["history", "questions"]) {
-      await jsonbColumn(table, "options");
-      await jsonbColumn(table, "qualityindicators");
-      await jsonbColumn(table, "extra");
-    }
-
-    await client.query("COMMIT");
-  } catch (e) {
-    await client.query("ROLLBACK");
-    throw e;
-  } finally {
-    client.release();
-  }
-};
-
-migrate();
diff --git a/src/backend/platforms/_example.ts b/src/backend/platforms/_example.ts
index 92119b9..3c5c3fe 100644
--- a/src/backend/platforms/_example.ts
+++ b/src/backend/platforms/_example.ts
@@ -2,7 +2,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "example";
@@ -24,9 +24,9 @@ async function fetchData() {
 
 async function processPredictions(predictions) {
   let results = await predictions.map((prediction) => {
-    let id = `${platformName}-${prediction.id}`;
-    let probability = prediction.probability;
-    let options = [
+    const id = `${platformName}-${prediction.id}`;
+    const probability = prediction.probability;
+    const options = [
       {
         name: "Yes",
         probability: probability,
@@ -38,19 +38,19 @@ async function processPredictions(predictions) {
         type: "PROBABILITY",
       },
     ];
-    let result = {
+    const result: FetchedQuestion = {
+      id,
       title: prediction.title,
       url: `https://example.com`,
       platform: platformName,
       description: prediction.description,
-      options: options,
-      timestamp: new Date().toISOString(),
+      options,
       qualityindicators: {
         stars: calculateStars(platformName, {
           /* some: somex, factors: factors */
         }),
-        other: prediction.otherx,
-        indicators: prediction.indicatorx,
+        // other: prediction.otherx,
+        // indicators: prediction.indicatorx,
       },
     };
     return result;
diff --git a/src/backend/platforms/betfair.ts b/src/backend/platforms/betfair.ts
index bbfd061..4af99fc 100644
--- a/src/backend/platforms/betfair.ts
+++ b/src/backend/platforms/betfair.ts
@@ -3,7 +3,7 @@ import axios from "axios";
 import https from "https";
 
 import { calculateStars } from "../utils/stars";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 const platformName = "betfair";
 
@@ -80,7 +80,7 @@ async function whipIntoShape(data) {
 async function processPredictions(data) {
   let predictions = await whipIntoShape(data);
   // console.log(JSON.stringify(predictions, null, 4))
-  let results: Question[] = predictions.map((prediction) => {
+  let results: FetchedQuestion[] = predictions.map((prediction) => {
     /* if(Math.floor(Math.random() * 10) % 20 ==0){
        console.log(JSON.stringify(prediction, null, 4))
     } */
@@ -126,7 +126,6 @@ async function processPredictions(data) {
       platform: platformName,
       description: description,
       options: options,
-      timestamp: new Date().toISOString(),
       qualityindicators: {
         stars: calculateStars(platformName, {
           volume: prediction.totalMatched,
diff --git a/src/backend/platforms/fantasyscotus.ts b/src/backend/platforms/fantasyscotus.ts
index 6feb947..2787373 100644
--- a/src/backend/platforms/fantasyscotus.ts
+++ b/src/backend/platforms/fantasyscotus.ts
@@ -2,7 +2,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 const platformName = "fantasyscotus";
 
@@ -67,7 +67,7 @@ async function processData(data) {
   let historicalPercentageCorrect = data.stats.pcnt_correct;
   let historicalProbabilityCorrect =
     Number(historicalPercentageCorrect.replace("%", "")) / 100;
-  let results: Question[] = [];
+  let results: FetchedQuestion[] = [];
   for (let event of events) {
     if (event.accuracy == "") {
       let id = `${platformName}-${event.id}`;
@@ -75,7 +75,7 @@ async function processData(data) {
       let predictionData = await getPredictionsData(event.docket_url);
       let pAffirm = predictionData.proportionAffirm;
       //let trackRecord = event.prediction.includes("Affirm") ? historicalProbabilityCorrect : 1-historicalProbabilityCorrect
-      let eventObject: Question = {
+      let eventObject: FetchedQuestion = {
         id: id,
         title: `In ${event.short_name}, the SCOTUS will affirm the lower court's decision`,
         url: `https://fantasyscotus.net/user-predictions${event.docket_url}`,
@@ -99,7 +99,6 @@ async function processData(data) {
             type: "PROBABILITY",
           },
         ],
-        timestamp: new Date().toISOString(),
         qualityindicators: {
           numforecasts: Number(predictionData.numForecasts),
           stars: calculateStars(platformName, {}),
diff --git a/src/backend/platforms/foretold.ts b/src/backend/platforms/foretold.ts
index 92a27bd..9adce77 100644
--- a/src/backend/platforms/foretold.ts
+++ b/src/backend/platforms/foretold.ts
@@ -2,7 +2,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 
@@ -61,7 +61,7 @@ export const foretold: Platform = {
   label: "Foretold",
   color: "#62520b",
   async fetcher() {
-    let results = [];
+    let results: FetchedQuestion[] = [];
     for (let community of highQualityCommunities) {
       let questions = await fetchAllCommunityQuestions(community);
       questions = questions.map((question) => question.node);
@@ -84,14 +84,13 @@ export const foretold: Platform = {
             },
           ];
         }
-        let result = {
-          id: id,
+        let result: FetchedQuestion = {
+          id,
           title: question.name,
           url: `https://www.foretold.io/c/${community}/m/${question.id}`,
           platform: platformName,
           description: "",
-          options: options,
-          timestamp: new Date().toISOString(),
+          options,
           qualityindicators: {
             numforecasts: Math.floor(Number(question.measurementCount) / 2),
             stars: calculateStars(platformName, {}),
diff --git a/src/backend/platforms/givewellopenphil.ts b/src/backend/platforms/givewellopenphil.ts
index e8170ac..22c37b8 100644
--- a/src/backend/platforms/givewellopenphil.ts
+++ b/src/backend/platforms/givewellopenphil.ts
@@ -47,12 +47,12 @@ async function main1() {
       );
     let description = "
 ({
       ...datum,
       platform: platformName,
-      timestamp: "2021-02-23",
+      timestamp: new Date("2021-02-23"),
     }));
     return dataWithDate;
   },
diff --git a/src/backend/platforms/goodjudgment.ts b/src/backend/platforms/goodjudgment.ts
index 17227fa..499e8f0 100644
--- a/src/backend/platforms/goodjudgment.ts
+++ b/src/backend/platforms/goodjudgment.ts
@@ -5,7 +5,7 @@ import tunnel from "tunnel";
 
 import { hash } from "../utils/hash";
 import { calculateStars } from "../utils/stars";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "goodjudgment";
@@ -57,7 +57,7 @@ export const goodjudgment: Platform = {
       .then((query) => query.data);
 
     // Processing
-    let results = [];
+    let results: FetchedQuestion[] = [];
     let jsonTable = Tabletojson.convert(content, { stripHtmlFromCells: false });
     jsonTable.shift(); // deletes first element
     jsonTable.pop(); // deletes last element
@@ -100,14 +100,13 @@ export const goodjudgment: Platform = {
         analysis = analysis ? analysis[0] : "";
         analysis = analysis ? analysis[0] : ""; // not a duplicate
         // console.log(analysis)
-        let standardObj = {
-          id: id,
-          title: title,
+        let standardObj: FetchedQuestion = {
+          id,
+          title,
           url: endpoint,
           platform: platformName,
-          description: description,
-          options: options,
-          timestamp: new Date().toISOString(),
+          description,
+          options,
           qualityindicators: {
             stars: calculateStars(platformName, {}),
           },
diff --git a/src/backend/platforms/goodjudgmentopen.ts b/src/backend/platforms/goodjudgmentopen.ts
index a4e73f0..a4e1087 100644
--- a/src/backend/platforms/goodjudgmentopen.ts
+++ b/src/backend/platforms/goodjudgmentopen.ts
@@ -114,7 +114,6 @@ async function fetchStats(questionUrl, cookie) {
   let result = {
     description: description,
     options: options,
-    timestamp: new Date().toISOString(),
     qualityindicators: {
       numforecasts: Number(numforecasts),
       numforecasters: Number(numforecasters),
diff --git a/src/backend/platforms/index.ts b/src/backend/platforms/index.ts
index bbf87bd..f9539cb 100644
--- a/src/backend/platforms/index.ts
+++ b/src/backend/platforms/index.ts
@@ -1,4 +1,6 @@
-import { pgUpsert } from "../database/pg-wrapper";
+import { Question } from "@prisma/client";
+
+import { prisma } from "../database/prisma";
 import { betfair } from "./betfair";
 import { fantasyscotus } from "./fantasyscotus";
 import { foretold } from "./foretold";
@@ -28,57 +30,23 @@ export interface QualityIndicators {
   tradevolume?: string;
   pool?: any;
   createdTime?: any;
+  shares_volume?: any;
+  yes_bid?: any;
+  yes_ask?: any;
+  spread?: any;
 }
 
-export interface Question {
-  id: string;
-  // "fantasyscotus-580"
-
-  title: string;
-  // "In Wooden v. U.S., the SCOTUS will affirm the lower court's decision"
-
-  url: string;
-  // "https://fantasyscotus.net/user-predictions/case/wooden-v-us/"
-
-  description: string;
-  // "62.50% (75 out of 120) of FantasySCOTUS players predict that the lower court's decision will be affirmed. FantasySCOTUS overall predicts an outcome of Affirm 6-3. Historically, FantasySCOTUS has chosen the correct side 50.00% of the time."
-  platform: string;
-  // "FantasySCOTUS"
-
-  options: any[];
-  /*
-  [
-    {
-      "name": "Yes",
-      "probability": 0.625,
-      "type": "PROBABILITY"
-    },
-    {
-      "name": "No",
-      "probability": 0.375,
-      "type": "PROBABILITY"
-    }
-  ]
-  */
-
-  timestamp: string;
-  // "2022-02-11T21:42:19.291Z"
-
-  stars?: number;
-  // 2
-
-  qualityindicators: QualityIndicators;
-  /*
-  {
-    "numforecasts": 120,
-    "stars": 2
-  }
-  */
-  extra?: any;
-}
+export type FetchedQuestion = Omit<
+  Question,
+  "extra" | "qualityindicators" | "timestamp"
+> & {
+  timestamp?: Date;
+  extra?: object; // required in DB but annoying to return empty; also this is slightly stricter than Prisma's JsonValue
+  qualityindicators: QualityIndicators; // slightly stronger type than Prisma's JsonValue
+};
 
 // fetcher should return null if platform failed to fetch questions for some reason
-export type PlatformFetcher = () => Promise;
+export type PlatformFetcher = () => Promise;
 
 export interface Platform {
   name: string; // short name for ids and `platform` db column, e.g. "xrisk"
@@ -95,13 +63,6 @@ export interface Platform {
 
 // export type PlatformFetcher = (options: FetchOptions) => Promise;
 
-// interface Platform {
-//   name: string;
-//   color?: string;
-//   longName: string;
-//   fetcher: PlatformFetcher;
-// }
-
 export const platforms: Platform[] = [
   betfair,
   fantasyscotus,
@@ -126,13 +87,23 @@ export const processPlatform = async (platform: Platform) => {
     console.log(`Platform ${platform.name} doesn't have a fetcher, skipping`);
     return;
   }
-  let results = await platform.fetcher();
+  const results = await platform.fetcher();
   if (results && results.length) {
-    await pgUpsert({
-      contents: results,
-      tableName: "questions",
-      replacePlatform: platform.name,
-    });
+    await prisma.$transaction([
+      prisma.question.deleteMany({
+        where: {
+          platform: platform.name,
+        },
+      }),
+      prisma.question.createMany({
+        data: results.map((q) => ({
+          extra: {},
+          timestamp: new Date(),
+          ...q,
+          qualityindicators: q.qualityindicators as object, // fighting typescript
+        })),
+      }),
+    ]);
     console.log("Done");
   } else {
     console.log(`Platform ${platform.name} didn't return any results`);
diff --git a/src/backend/platforms/infer.ts b/src/backend/platforms/infer.ts
index 23a78d6..cf62fd0 100644
--- a/src/backend/platforms/infer.ts
+++ b/src/backend/platforms/infer.ts
@@ -5,7 +5,7 @@ import { applyIfSecretExists } from "../utils/getSecrets";
 import { measureTime } from "../utils/measureTime";
 import { calculateStars } from "../utils/stars";
 import toMarkdown from "../utils/toMarkdown";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "infer";
@@ -105,7 +105,6 @@ async function fetchStats(questionUrl, cookie) {
   let result = {
     description: description,
     options: options,
-    timestamp: new Date().toISOString(),
     qualityindicators: {
       numforecasts: Number(numforecasts),
       numforecasters: Number(numforecasters),
@@ -147,7 +146,7 @@ function sleep(ms) {
 async function infer_inner(cookie: string) {
   let i = 1;
   let response = await fetchPage(i, cookie);
-  let results: Question[] = [];
+  let results: FetchedQuestion[] = [];
 
   await measureTime(async () => {
     // console.log("Downloading... This might take a couple of minutes. Results will be shown.")
@@ -178,7 +177,7 @@ async function infer_inner(cookie: string) {
           let questionNumRegex = new RegExp("questions/([0-9]+)");
           let questionNum = url.match(questionNumRegex)[1]; //.split("questions/")[1].split("-")[0];
           let id = `${platformName}-${questionNum}`;
-          let question: Question = {
+          let question: FetchedQuestion = {
             id: id,
             title: title,
             description: moreinfo.description,
diff --git a/src/backend/platforms/kalshi.ts b/src/backend/platforms/kalshi.ts
index c785a6a..6330298 100644
--- a/src/backend/platforms/kalshi.ts
+++ b/src/backend/platforms/kalshi.ts
@@ -2,7 +2,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "kalshi";
@@ -22,8 +22,8 @@ async function processMarkets(markets) {
   // console.log(markets)
   markets = markets.filter((market) => market.close_date > dateNow);
   let results = await markets.map((market) => {
-    let probability = market.last_price / 100;
-    let options = [
+    const probability = market.last_price / 100;
+    const options = [
       {
         name: "Yes",
         probability: probability,
@@ -35,15 +35,14 @@ async function processMarkets(markets) {
         type: "PROBABILITY",
       },
     ];
-    let id = `${platformName}-${market.id}`;
-    let result = {
-      id: id,
+    const id = `${platformName}-${market.id}`;
+    const result: FetchedQuestion = {
+      id,
       title: market.title.replaceAll("*", ""),
       url: `https://kalshi.com/markets/${market.ticker_name}`,
       platform: platformName,
       description: `${market.settle_details}. The resolution source is: ${market.ranged_group_name} (${market.settle_source_url})`,
-      options: options,
-      timestamp: new Date().toISOString(),
+      options,
       qualityindicators: {
         stars: calculateStars(platformName, {
           shares_volume: market.volume,
diff --git a/src/backend/platforms/manifold.ts b/src/backend/platforms/manifold.ts
index 8304c61..ab09aa2 100644
--- a/src/backend/platforms/manifold.ts
+++ b/src/backend/platforms/manifold.ts
@@ -2,7 +2,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "manifold";
@@ -23,7 +23,7 @@ async function fetchData() {
   return response;
 }
 
-function showStatistics(results: Question[]) {
+function showStatistics(results: FetchedQuestion[]) {
   console.log(`Num unresolved markets: ${results.length}`);
   let sum = (arr) => arr.reduce((tally, a) => tally + a, 0);
   let num2StarsOrMore = results.filter(
@@ -44,7 +44,7 @@ function showStatistics(results: Question[]) {
 }
 
 async function processPredictions(predictions) {
-  let results: Question[] = await predictions.map((prediction) => {
+  let results: FetchedQuestion[] = await predictions.map((prediction) => {
     let id = `${platformName}-${prediction.id}`; // oops, doesn't match platform name
     let probability = prediction.probability;
     let options = [
@@ -59,14 +59,13 @@ async function processPredictions(predictions) {
         type: "PROBABILITY",
       },
     ];
-    const result: Question = {
+    const result: FetchedQuestion = {
       id: id,
       title: prediction.question,
       url: prediction.url,
       platform: platformName,
       description: prediction.description,
       options: options,
-      timestamp: new Date().toISOString(),
       qualityindicators: {
         stars: calculateStars(platformName, {
           volume7Days: prediction.volume7Days,
@@ -86,7 +85,7 @@ async function processPredictions(predictions) {
   });
 
   const unresolvedResults = results.filter(
-    (result) => !result.extra.isResolved
+    (result) => !(result.extra as any).isResolved
   );
   return unresolvedResults;
 }
diff --git a/src/backend/platforms/metaculus.ts b/src/backend/platforms/metaculus.ts
index 76ac37f..a567510 100644
--- a/src/backend/platforms/metaculus.ts
+++ b/src/backend/platforms/metaculus.ts
@@ -3,7 +3,7 @@ import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
 import toMarkdown from "../utils/toMarkdown";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "metaculus";
@@ -148,14 +148,13 @@ export const metaculus: Platform = {
               ];
             }
             let id = `${platformName}-${result.id}`;
-            let interestingInfo = {
-              id: id,
+            let interestingInfo: FetchedQuestion = {
+              id,
               title: result.title,
               url: "https://www.metaculus.com" + result.page_url,
               platform: platformName,
-              description: description,
-              options: options,
-              timestamp: new Date().toISOString(),
+              description,
+              options,
               qualityindicators: {
                 numforecasts: Number(result.number_of_predictions),
                 stars: calculateStars(platformName, {
diff --git a/src/backend/platforms/polymarket.ts b/src/backend/platforms/polymarket.ts
index 72c53f1..a91c42c 100644
--- a/src/backend/platforms/polymarket.ts
+++ b/src/backend/platforms/polymarket.ts
@@ -2,7 +2,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "polymarket";
@@ -68,7 +68,7 @@ export const polymarket: Platform = {
   label: "PolyMarket",
   color: "#00314e",
   async fetcher() {
-    let results: Question[] = [];
+    let results: FetchedQuestion[] = [];
     let webpageEndpointData = await fetchAllContractInfo();
     for (let marketInfo of webpageEndpointData) {
       let address = marketInfo.marketMakerAddress;
@@ -102,14 +102,13 @@ export const polymarket: Platform = {
             });
           }
 
-          let result: Question = {
+          let result: FetchedQuestion = {
             id: id,
             title: marketInfo.question,
             url: "https://polymarket.com/market/" + marketInfo.slug,
             platform: platformName,
             description: marketInfo.description,
             options: options,
-            timestamp: new Date().toISOString(),
             qualityindicators: {
               numforecasts: numforecasts.toFixed(0),
               liquidity: liquidity.toFixed(2),
diff --git a/src/backend/platforms/predictit.ts b/src/backend/platforms/predictit.ts
index 55e44cc..0233ec1 100644
--- a/src/backend/platforms/predictit.ts
+++ b/src/backend/platforms/predictit.ts
@@ -2,7 +2,7 @@ import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
 import toMarkdown from "../utils/toMarkdown";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 const platformName = "predictit";
 
@@ -53,7 +53,7 @@ export const predictit: Platform = {
     }));
     // console.log(markets)
 
-    let results = [];
+    let results: FetchedQuestion[] = [];
     for (let market of markets) {
       // console.log(market.name)
       let id = `${platformName}-${market.id}`;
@@ -96,17 +96,16 @@ export const predictit: Platform = {
         ];
       }
 
-      let obj = {
-        id: id,
+      const obj: FetchedQuestion = {
+        id,
         title: market["name"],
         url: market.url,
         platform: platformName,
-        description: description,
-        options: options,
-        timestamp: new Date().toISOString(),
+        description,
+        options,
         qualityindicators: {
           stars: calculateStars(platformName, {}),
-          shares_volume: shares_volume,
+          shares_volume,
         },
       };
       // console.log(obj)
diff --git a/src/backend/platforms/rootclaim.ts b/src/backend/platforms/rootclaim.ts
index d195cd7..1a1feff 100644
--- a/src/backend/platforms/rootclaim.ts
+++ b/src/backend/platforms/rootclaim.ts
@@ -3,7 +3,7 @@ import { JSDOM } from "jsdom";
 
 import { calculateStars } from "../utils/stars";
 import toMarkdown from "../utils/toMarkdown";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 const platformName = "rootclaim";
 const jsonEndpoint =
@@ -50,7 +50,7 @@ export const rootclaim: Platform = {
   color: "#0d1624",
   async fetcher() {
     const claims = await fetchAllRootclaims();
-    const results: Question[] = [];
+    const results: FetchedQuestion[] = [];
 
     for (const claim of claims) {
       const id = `${platformName}-${claim.slug.toLowerCase()}`;
@@ -71,14 +71,13 @@ export const rootclaim: Platform = {
 
       const description = await fetchDescription(url, claim.isclaim);
 
-      let obj: Question = {
+      let obj: FetchedQuestion = {
         id,
         title: toMarkdown(claim.question).replace("\n", ""),
         url,
         platform: platformName,
         description: toMarkdown(description).replace("'", "'"),
         options: options,
-        timestamp: new Date().toISOString(),
         qualityindicators: {
           numforecasts: 1,
           stars: calculateStars(platformName, {}),
diff --git a/src/backend/platforms/smarkets.ts b/src/backend/platforms/smarkets.ts
index 845caea..1b95af8 100644
--- a/src/backend/platforms/smarkets.ts
+++ b/src/backend/platforms/smarkets.ts
@@ -1,7 +1,7 @@
 import axios from "axios";
 
 import { calculateStars } from "../utils/stars";
-import { Platform, Question } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "smarkets";
@@ -159,14 +159,14 @@ export const smarkets: Platform = {
       name = name+ (contractName=="Yes"?'':` (${contracts["contracts"][0].name})`)
     }
     */
-      let result: Question = {
+      let result: FetchedQuestion = {
         id: id,
         title: name,
         url: "https://smarkets.com/event/" + market.event_id + market.slug,
         platform: platformName,
         description: market.description,
         options: options,
-        timestamp: new Date().toISOString(),
+        timestamp: new Date(),
         qualityindicators: {
           stars: calculateStars(platformName, {}),
         },
diff --git a/src/backend/platforms/wildeford.ts b/src/backend/platforms/wildeford.ts
index e628516..7778b04 100644
--- a/src/backend/platforms/wildeford.ts
+++ b/src/backend/platforms/wildeford.ts
@@ -4,7 +4,7 @@ import { GoogleSpreadsheet } from "google-spreadsheet";
 import { applyIfSecretExists } from "../utils/getSecrets";
 import { hash } from "../utils/hash";
 import { calculateStars } from "../utils/stars";
-import { Platform } from "./";
+import { FetchedQuestion, Platform } from "./";
 
 /* Definitions */
 const platformName = "wildeford";
@@ -88,16 +88,14 @@ async function processPredictions(predictions) {
         type: "PROBABILITY",
       },
     ];
-    let result = {
-      id: id,
-      title: title,
+    let result: FetchedQuestion = {
+      id,
+      title,
       url: prediction["url"],
       platform: platformName,
       description: prediction["Notes"] || "",
-      options: options,
-      timestamp: new Date(
-        Date.parse(prediction["Prediction Date"] + "Z")
-      ).toISOString(),
+      options,
+      timestamp: new Date(Date.parse(prediction["Prediction Date"] + "Z")),
       qualityindicators: {
         stars: calculateStars(platformName, null),
       },
diff --git a/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts b/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts
index 1c044cf..c02315e 100644
--- a/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts
+++ b/src/backend/utils/evaluations/pullForecastsToCSVForRating.ts
@@ -1,7 +1,7 @@
 /* Imports */
 import fs from "fs";
 
-import { pgRead } from "../../database/pg-wrapper";
+import { prisma } from "../../database/prisma";
 
 /* Definitions */
 
@@ -24,7 +24,7 @@ const main = async () => {
     "PredictIt",
     "Rootclaim",
   ];
-  const json = await pgRead({ tableName: "questions" });
+  const json = await prisma.question.findMany({});
   console.log(json.length);
   //let uniquePlatforms = [...new Set(json.map(forecast => forecast.platform))]
   //console.log(uniquePlatforms)
diff --git a/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts b/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts
index 6cc2231..cbc048d 100644
--- a/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts
+++ b/src/backend/utils/evaluations/pullMetaculusForecastsToCSVForRating.ts
@@ -2,7 +2,7 @@
 import fs from "fs";
 
 import { shuffleArray } from "../../../utils";
-import { pgRead } from "../../database/pg-wrapper";
+import { prisma } from "../../database/prisma";
 
 /* Definitions */
 
@@ -18,7 +18,7 @@ let getQualityIndicators = (question) =>
 
 let main = async () => {
   let highQualityPlatforms = ["Metaculus"]; // ['CSET-foretell', 'Foretold', 'Good Judgment Open', 'Metaculus', 'PredictIt', 'Rootclaim']
-  let json = await pgRead({ tableName: "questions" });
+  let json = await prisma.question.findMany({});
   console.log(json.length);
   //let uniquePlatforms = [...new Set(json.map(question => question.platform))]
   //console.log(uniquePlatforms)
diff --git a/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts b/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts
index 40ddd9e..bf73046 100644
--- a/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts
+++ b/src/backend/utils/misc/process-forecasts-from-old-givewellopenphil.ts
@@ -38,7 +38,7 @@ for (let datum of data) {
 		*/
     timestamp: "2021-02-23T15∶21∶37.005Z", //new Date().toISOString(),
     qualityindicators: {
-      stars: datum.qualityindicators.stars, //datum["stars"],
+      stars: datum.qualityindicators.stars,
     },
   };
   results.push(result);
diff --git a/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts b/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts
index aa53194..1c1f327 100644
--- a/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts
+++ b/src/backend/utils/misc/process-forecasts-from-old-xrisk.ts
@@ -40,7 +40,7 @@ ${datum["description"]}`
     ],
     timestamp: new Date().toISOString(),
     qualityindicators: {
-      stars: 2, //datum["stars"]
+      stars: 2,
     },
   };
   results.push(result);
diff --git a/src/backend/utils/misc/process-forecasts-from-xrisk.ts b/src/backend/utils/misc/process-forecasts-from-xrisk.ts
index 5ea0c97..dc5c847 100644
--- a/src/backend/utils/misc/process-forecasts-from-xrisk.ts
+++ b/src/backend/utils/misc/process-forecasts-from-xrisk.ts
@@ -23,7 +23,7 @@ for (let datum of data) {
     options: datum.options,
     timestamp: datum.timestamps,
     qualityindicators: {
-      stars: 2, //datum["stars"]
+      stars: 2,
     },
   };
   results.push(result);
diff --git a/src/backend/utils/misc/process-forecasts-into-elicit.ts b/src/backend/utils/misc/process-forecasts-into-elicit.ts
index c6ce4fb..8cf28b7 100644
--- a/src/backend/utils/misc/process-forecasts-into-elicit.ts
+++ b/src/backend/utils/misc/process-forecasts-into-elicit.ts
@@ -1,7 +1,8 @@
 /* Imports */
 import fs from "fs";
 
-import { pgRead } from "../../database/pg-wrapper";
+import { prisma } from "../../database/prisma";
+import { QualityIndicators } from "../../platforms";
 
 /* Definitions */
 let locationData = "./data/";
@@ -9,8 +10,8 @@ let locationData = "./data/";
 /* Body */
 // let rawdata =  fs.readFileSync("./data/merged-questions.json") // run from topmost folder, not from src
 async function main() {
-  let data = await pgRead({ tableName: "questions" }); //JSON.parse(rawdata)
-  let processDescription = (description) => {
+  const data = await prisma.question.findMany({});
+  const processDescription = (description) => {
     if (description == null || description == undefined || description == "") {
       return "";
     } else {
@@ -32,14 +33,14 @@ async function main() {
   };
 
   let results = [];
-  for (let datum of data) {
+  for (const datum of data) {
     // do something
-    let description = processDescription(datum["description"]);
-    let forecasts = datum["qualityindicators"]
-      ? datum["qualityindicators"].numforecasts
+    const description = processDescription(datum["description"]);
+    const forecasts = datum["qualityindicators"]
+      ? (datum["qualityindicators"] as object as QualityIndicators).numforecasts
       : "unknown";
-    let stars = datum["qualityindicators"]
-      ? datum["qualityindicators"].stars
+    const stars = datum["qualityindicators"]
+      ? (datum["qualityindicators"] as object as QualityIndicators).stars
       : 2;
     results.push("Title: " + datum["title"]);
     results.push("URL: " + datum["url"]);
diff --git a/src/web/worker/searchGuesstimate.ts b/src/web/worker/searchGuesstimate.ts
index 8af4a4c..51b20d3 100644
--- a/src/web/worker/searchGuesstimate.ts
+++ b/src/web/worker/searchGuesstimate.ts
@@ -43,11 +43,10 @@ export default async function searchGuesstimate(
       description,
       options: [],
       qualityindicators: {
-        stars: stars,
+        stars,
         numforecasts: 1,
         numforecasters: 1,
       },
-      stars,
       extra: {
         visualization: model.big_screenshot,
       },
diff --git a/src/web/worker/searchWithAlgolia.ts b/src/web/worker/searchWithAlgolia.ts
index ce89420..9eba5b4 100644
--- a/src/web/worker/searchWithAlgolia.ts
+++ b/src/web/worker/searchWithAlgolia.ts
@@ -116,7 +116,6 @@ export default async function searchWithAlgolia({
           },
         ],
         timestamp: `${new Date().toISOString().slice(0, 10)}`,
-        stars: 5, // legacy
         qualityindicators: {
           numforecasts: 1,
           numforecasters: 1,
@@ -148,7 +147,6 @@ export default async function searchWithAlgolia({
           },
         ],
         timestamp: `${new Date().toISOString().slice(0, 10)}`,
-        stars: 5, // legacy
         qualityindicators: {
           numforecasts: 1,
           numforecasters: 1,
@@ -183,7 +181,6 @@ export default async function searchWithAlgolia({
         },
       ],
       timestamp: `${new Date().toISOString().slice(0, 10)}`,
-      stars: 1, // legacy
       qualityindicators: {
         numforecasts: 1,
         numforecasters: 1,