Consolidate frontend REST calls with GraphQL
Some checks failed
Deploy with Docker Compose / deploy (push) Failing after 1s

Replace 5 separate REST calls on home page load with a single GraphQL
query. Add homeData store that fetches posts, favorites, activities,
spotify, and auth in one request. Convert all admin mutations and
auth flows to use GraphQL. Add album images to Spotify GraphQL schema.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 15:29:04 +00:00
parent 36817277f9
commit 0360b1f7f1
40 changed files with 8963 additions and 157 deletions

View File

@@ -2,7 +2,7 @@
import Button from "@/components/input/Button.vue";
import { ref } from "vue";
import axios from "axios";
import { gql } from "@/graphql";
const type = ref("");
const name = ref("");
@@ -10,15 +10,14 @@ const link = ref("");
async function post() {
try {
const res = await axios.post("/api/activity", {
type: type.value,
name: name.value,
link: link.value || undefined,
});
const data = await gql(
`mutation CreateActivity($input: CreateActivityInput!) { createActivity(input: $input) { id } }`,
{ input: { type: type.value, name: name.value, link: link.value || undefined } },
);
type.value = "";
name.value = "";
link.value = "";
console.log(res.data);
console.log(data.createActivity);
} catch (err) {
console.error(err);
}

View File

@@ -2,7 +2,7 @@
import Button from "@/components/input/Button.vue";
import { ref } from "vue";
import axios from "axios";
import { gql } from "@/graphql";
const type = ref("");
const name = ref("");
@@ -10,15 +10,14 @@ const link = ref("");
async function post() {
try {
const res = await axios.post("/api/favorites", {
type: type.value,
name: name.value,
link: link.value || undefined,
});
const data = await gql(
`mutation CreateFavorite($input: CreateFavoriteInput!) { createFavorite(input: $input) { id } }`,
{ input: { type: type.value, name: name.value, link: link.value || undefined } },
);
type.value = "";
name.value = "";
link.value = "";
console.log(res.data);
console.log(data.createFavorite);
} catch (err) {
console.error(err);
}

View File

@@ -1,20 +1,20 @@
<script setup>
import Button from "@/components/input/Button.vue";
import { ref } from "vue";
import axios from "axios";
import { gql } from "@/graphql";
const title = ref("");
const content = ref("");
async function post() {
try {
const res = await axios.post("/api/posts", {
title: title.value,
content: content.value,
});
const data = await gql(
`mutation CreatePost($input: CreatePostInput!) { createPost(input: $input) { id } }`,
{ input: { title: title.value, content: content.value } },
);
title.value = "";
content.value = "";
console.log(res.data);
console.log(data.createPost);
} catch (err) {
console.error(err);
}

View File

@@ -2,7 +2,7 @@
import Button from "@/components/input/Button.vue";
import { ref } from "vue";
import { useAuthStore } from "@/stores/auth";
import axios from "axios";
import { gql } from "@/graphql";
const auth = useAuthStore();
const username = ref("");
@@ -14,15 +14,15 @@ async function handleCreate() {
message.value = "";
error.value = "";
try {
const res = await axios.post("/api/user", {
username: username.value,
password: password.value,
});
message.value = `User "${res.data.username}" created successfully.`;
const data = await gql(
`mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id username } }`,
{ input: { username: username.value, password: password.value } },
);
message.value = `User "${data.createUser.username}" created successfully.`;
username.value = "";
password.value = "";
} catch (err) {
error.value = err.response?.data?.message || "Failed to create user.";
error.value = err.message || "Failed to create user.";
}
}
</script>

View File

@@ -2,15 +2,15 @@
import Button from "@/components/input/Button.vue";
import { ref, onMounted } from "vue";
import { useAuthStore } from "@/stores/auth";
import axios from "axios";
import { gql } from "@/graphql";
const auth = useAuthStore();
const users = ref([]);
async function fetchUsers() {
try {
const res = await axios.get("/api/user");
users.value = res.data;
const data = await gql(`query { users { id username admin } }`);
users.value = data.users;
} catch (err) {
console.error(err);
}
@@ -18,8 +18,8 @@ async function fetchUsers() {
async function toggleAdmin(user) {
try {
const res = await auth.setUserAdmin(user.id, !user.admin);
user.admin = res.admin;
const data = await auth.setUserAdmin(user.id, !user.admin);
user.admin = data.admin;
} catch (err) {
console.error(err);
}

View File

@@ -33,7 +33,7 @@ onUnmounted(() => {
<Transition name="fade">
<div
@click="nextSong"
:key="song.track.id"
:key="song.track.name"
class="flex flex-col items-center"
>
<Header>Listening To</Header>

View File

@@ -3,11 +3,9 @@ import Wip from "@/components/util/Wip.vue";
</script>
<template>
<main class="items-center flex flex-col">
<div
class="a4page-portrait items-center bdr-1 flex flex-col relative overflow-scroll"
>
<Wip />
</div>
</main>
<main class="items-center flex flex-col">
<div class="a4page-portrait items-center bdr-1 flex flex-col relative overflow-scroll">
<Wip />
</div>
</main>
</template>