From 2889b562e64993482bd13fd806af8ed0865bab8b Mon Sep 17 00:00:00 2001
From: nsfisis <nsfisis@gmail.com>
Date: Sat, 14 Feb 2026 11:52:56 +0900
Subject: refactor: migrate API from GraphQL to REST (TypeSpec/OpenAPI)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Replace the entire GraphQL stack (gqlgen, urql, graphql-codegen) with a
TypeSpec → OpenAPI 3.x pipeline using oapi-codegen for Go server stubs
and openapi-fetch + openapi-typescript for the frontend client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 frontend/src/components/ArticleItem.tsx | 33 ++++++++++++---------------------
 1 file changed, 12 insertions(+), 21 deletions(-)

(limited to 'frontend/src/components/ArticleItem.tsx')

diff --git a/frontend/src/components/ArticleItem.tsx b/frontend/src/components/ArticleItem.tsx
index dbdaf44..37664a9 100644
--- a/frontend/src/components/ArticleItem.tsx
+++ b/frontend/src/components/ArticleItem.tsx
@@ -1,30 +1,16 @@
 import { faCheck, faCircle } from "@fortawesome/free-solid-svg-icons";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { useMutation } from "urql";
-import type {
-	GetReadArticlesQuery,
-	GetUnreadArticlesQuery,
-} from "../graphql/generated/graphql";
-import {
-	MarkArticleReadDocument,
-	MarkArticleUnreadDocument,
-} from "../graphql/generated/graphql";
+import type { components } from "../api/generated";
+import { api } from "../services/api-client";
 
-type Article =
-	| GetUnreadArticlesQuery["unreadArticles"]["articles"][number]
-	| GetReadArticlesQuery["readArticles"]["articles"][number];
+type Article = components["schemas"]["Article"];
 
 interface Props {
 	article: Article;
 	onReadChange?: (articleId: string, isRead: boolean) => void;
 }
 
-const urqlContextArticle = { additionalTypenames: ["Article"] };
-
 export function ArticleItem({ article, onReadChange }: Props) {
-	const [, markArticleRead] = useMutation(MarkArticleReadDocument);
-	const [, markArticleUnread] = useMutation(MarkArticleUnreadDocument);
-
 	const handleToggleRead = async (
 		articleId: string,
 		isCurrentlyRead: boolean,
@@ -33,18 +19,23 @@ export function ArticleItem({ article, onReadChange }: Props) {
 		onReadChange?.(articleId, newReadState);
 
 		if (isCurrentlyRead) {
-			await markArticleUnread({ id: articleId }, urqlContextArticle);
+			await api.POST("/api/articles/{articleId}/unread", {
+				params: { path: { articleId } },
+			});
 		} else {
-			await markArticleRead({ id: articleId }, urqlContextArticle);
+			await api.POST("/api/articles/{articleId}/read", {
+				params: { path: { articleId } },
+			});
 		}
 	};
 
 	const handleArticleClick = async (article: Article) => {
-		// Open article in new tab and mark as read if it's unread
 		window.open(article.url, "_blank", "noreferrer");
 		if (!article.isRead) {
 			onReadChange?.(article.id, true);
-			await markArticleRead({ id: article.id }, urqlContextArticle);
+			await api.POST("/api/articles/{articleId}/read", {
+				params: { path: { articleId: article.id } },
+			});
 		}
 	};
 
-- 
cgit v1.3-1-g0d28

