diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-14 11:52:56 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-14 11:53:08 +0900 |
| commit | 2889b562e64993482bd13fd806af8ed0865bab8b (patch) | |
| tree | 39400ac4d994fb33d2c544e7d4b9d98f8ecbd86a /typespec/main.tsp | |
| parent | e216c3bc97994b4172d15d52b46d5f6b75f35ea4 (diff) | |
| download | feedaka-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.tsp | 185 |
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; + }; +} |
