aboutsummaryrefslogtreecommitdiffhomepage
path: root/typespec/main.tsp
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-14 11:52:56 +0900
committernsfisis <nsfisis@gmail.com>2026-02-14 11:53:08 +0900
commit2889b562e64993482bd13fd806af8ed0865bab8b (patch)
tree39400ac4d994fb33d2c544e7d4b9d98f8ecbd86a /typespec/main.tsp
parente216c3bc97994b4172d15d52b46d5f6b75f35ea4 (diff)
downloadfeedaka-2889b562e64993482bd13fd806af8ed0865bab8b.tar.gz
feedaka-2889b562e64993482bd13fd806af8ed0865bab8b.tar.zst
feedaka-2889b562e64993482bd13fd806af8ed0865bab8b.zip
refactor: migrate API from GraphQL to REST (TypeSpec/OpenAPI)
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>
Diffstat (limited to 'typespec/main.tsp')
-rw-r--r--typespec/main.tsp185
1 files changed, 185 insertions, 0 deletions
diff --git a/typespec/main.tsp b/typespec/main.tsp
new file mode 100644
index 0000000..89ccd6a
--- /dev/null
+++ b/typespec/main.tsp
@@ -0,0 +1,185 @@
+import "@typespec/http";
+import "@typespec/openapi3";
+
+using TypeSpec.Http;
+
+@service(#{ title: "Feedaka API" })
+@server("http://localhost:8080", "Development server")
+namespace Feedaka;
+
+// ── Models ──────────────────────────────────────────────────────────
+
+model Feed {
+ id: string;
+ url: string;
+ title: string;
+ fetchedAt: string;
+ isSubscribed: boolean;
+ unreadCount: int32;
+}
+
+model Article {
+ id: string;
+ feedId: string;
+ guid: string;
+ title: string;
+ url: string;
+ isRead: boolean;
+ feed: ArticleFeed;
+}
+
+model ArticleFeed {
+ id: string;
+ url: string;
+ title: string;
+ isSubscribed: boolean;
+}
+
+model PageInfo {
+ hasNextPage: boolean;
+ endCursor?: string;
+}
+
+model ArticleConnection {
+ articles: Article[];
+ pageInfo: PageInfo;
+}
+
+model User {
+ id: string;
+ username: string;
+}
+
+model ErrorResponse {
+ message: string;
+}
+
+// ── Auth ────────────────────────────────────────────────────────────
+
+model LoginRequest {
+ username: string;
+ password: string;
+}
+
+model LoginResponse {
+ user: User;
+}
+
+@route("/api/auth")
+namespace Auth {
+ @post
+ @route("/login")
+ op login(@body body: LoginRequest): LoginResponse | {
+ @statusCode statusCode: 401;
+ @body body: ErrorResponse;
+ };
+
+ @post
+ @route("/logout")
+ op logout(): {
+ @statusCode statusCode: 204;
+ } | {
+ @statusCode statusCode: 401;
+ @body body: ErrorResponse;
+ };
+
+ @get
+ @route("/me")
+ op getCurrentUser(): User | {
+ @statusCode statusCode: 401;
+ @body body: ErrorResponse;
+ };
+}
+
+// ── Feeds ───────────────────────────────────────────────────────────
+
+model AddFeedRequest {
+ url: string;
+}
+
+@route("/api/feeds")
+namespace Feeds {
+ @get
+ op listFeeds(): Feed[];
+
+ @post
+ op addFeed(@body body: AddFeedRequest): {
+ @statusCode statusCode: 201;
+ @body body: Feed;
+ } | {
+ @statusCode statusCode: 400;
+ @body body: ErrorResponse;
+ };
+
+ @get
+ @route("/{feedId}")
+ op getFeed(@path feedId: string): Feed | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+
+ @delete
+ @route("/{feedId}")
+ op unsubscribeFeed(@path feedId: string): {
+ @statusCode statusCode: 204;
+ } | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+
+ @post
+ @route("/{feedId}/read")
+ op markFeedRead(@path feedId: string): Feed | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+
+ @post
+ @route("/{feedId}/unread")
+ op markFeedUnread(@path feedId: string): Feed | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+}
+
+// ── Articles ────────────────────────────────────────────────────────
+
+@route("/api/articles")
+namespace Articles {
+ @get
+ @route("/unread")
+ op listUnreadArticles(
+ @query feedId?: string,
+ @query after?: string,
+ @query first?: int32,
+ ): ArticleConnection;
+
+ @get
+ @route("/read")
+ op listReadArticles(
+ @query feedId?: string,
+ @query after?: string,
+ @query first?: int32,
+ ): ArticleConnection;
+
+ @get
+ @route("/{articleId}")
+ op getArticle(@path articleId: string): Article | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+
+ @post
+ @route("/{articleId}/read")
+ op markArticleRead(@path articleId: string): Article | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+
+ @post
+ @route("/{articleId}/unread")
+ op markArticleUnread(@path articleId: string): Article | {
+ @statusCode statusCode: 404;
+ @body body: ErrorResponse;
+ };
+}