aboutsummaryrefslogtreecommitdiffhomepage
path: root/frontend/src/graphql
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-07-12 08:59:01 +0900
committernsfisis <nsfisis@gmail.com>2025-07-12 10:18:01 +0900
commit9da56e3023af305ba7c5fd49caab60ac8bb57100 (patch)
treeee6ada3798ffafac1742dbcdff2fbb6326640498 /frontend/src/graphql
parent1e6d528f54744a7fd76ba5cac4e63d8b5abd5fd6 (diff)
downloadfeedaka-9da56e3023af305ba7c5fd49caab60ac8bb57100.tar.gz
feedaka-9da56e3023af305ba7c5fd49caab60ac8bb57100.tar.zst
feedaka-9da56e3023af305ba7c5fd49caab60ac8bb57100.zip
feat(frontend): setup GraphQL code generation
Diffstat (limited to 'frontend/src/graphql')
-rw-r--r--frontend/src/graphql/generated/fragment-masking.ts87
-rw-r--r--frontend/src/graphql/generated/gql.ts52
-rw-r--r--frontend/src/graphql/generated/graphql.ts212
-rw-r--r--frontend/src/graphql/generated/index.ts2
-rw-r--r--frontend/src/graphql/mutations.graphql52
-rw-r--r--frontend/src/graphql/queries.graphql73
6 files changed, 478 insertions, 0 deletions
diff --git a/frontend/src/graphql/generated/fragment-masking.ts b/frontend/src/graphql/generated/fragment-masking.ts
new file mode 100644
index 0000000..743a364
--- /dev/null
+++ b/frontend/src/graphql/generated/fragment-masking.ts
@@ -0,0 +1,87 @@
+/* eslint-disable */
+import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
+import type { FragmentDefinitionNode } from 'graphql';
+import type { Incremental } from './graphql';
+
+
+export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
+ infer TType,
+ any
+>
+ ? [TType] extends [{ ' $fragmentName'?: infer TKey }]
+ ? TKey extends string
+ ? { ' $fragmentRefs'?: { [key in TKey]: TType } }
+ : never
+ : never
+ : never;
+
+// return non-nullable if `fragmentType` is non-nullable
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
+): TType;
+// return nullable if `fragmentType` is undefined
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined
+): TType | undefined;
+// return nullable if `fragmentType` is nullable
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null
+): TType | null;
+// return nullable if `fragmentType` is nullable or undefined
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
+): TType | null | undefined;
+// return array of non-nullable if `fragmentType` is array of non-nullable
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>
+): Array<TType>;
+// return array of nullable if `fragmentType` is array of nullable
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
+): Array<TType> | null | undefined;
+// return readonly array of non-nullable if `fragmentType` is array of non-nullable
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
+): ReadonlyArray<TType>;
+// return readonly array of nullable if `fragmentType` is array of nullable
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
+): ReadonlyArray<TType> | null | undefined;
+export function useFragment<TType>(
+ _documentNode: DocumentTypeDecoration<TType, any>,
+ fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
+): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
+ return fragmentType as any;
+}
+
+
+export function makeFragmentData<
+ F extends DocumentTypeDecoration<any, any>,
+ FT extends ResultOf<F>
+>(data: FT, _fragment: F): FragmentType<F> {
+ return data as FragmentType<F>;
+}
+export function isFragmentReady<TQuery, TFrag>(
+ queryNode: DocumentTypeDecoration<TQuery, any>,
+ fragmentNode: TypedDocumentNode<TFrag>,
+ data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
+): data is FragmentType<typeof fragmentNode> {
+ const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
+ ?.deferredFields;
+
+ if (!deferredFields) return true;
+
+ const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
+ const fragName = fragDef?.name?.value;
+
+ const fields = (fragName && deferredFields[fragName]) || [];
+ return fields.length > 0 && fields.every(field => data && field in data);
+}
diff --git a/frontend/src/graphql/generated/gql.ts b/frontend/src/graphql/generated/gql.ts
new file mode 100644
index 0000000..ae8c1e1
--- /dev/null
+++ b/frontend/src/graphql/generated/gql.ts
@@ -0,0 +1,52 @@
+/* eslint-disable */
+import * as types from './graphql';
+import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
+
+/**
+ * Map of all GraphQL operations in the project.
+ *
+ * This map has several performance disadvantages:
+ * 1. It is not tree-shakeable, so it will include all operations in the project.
+ * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
+ * 3. It does not support dead code elimination, so it will add unused operations.
+ *
+ * Therefore it is highly recommended to use the babel or swc plugin for production.
+ * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size
+ */
+type Documents = {
+ "mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}": typeof types.AddFeedDocument,
+ "query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}": typeof types.GetFeedsDocument,
+};
+const documents: Documents = {
+ "mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}": types.AddFeedDocument,
+ "query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}": types.GetFeedsDocument,
+};
+
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ *
+ *
+ * @example
+ * ```ts
+ * const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
+ * ```
+ *
+ * The query argument is unknown!
+ * Please regenerate the types.
+ */
+export function graphql(source: string): unknown;
+
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}"): (typeof documents)["mutation AddFeed($url: String!) {\n addFeed(url: $url) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation RemoveFeed($id: ID!) {\n removeFeed(id: $id)\n}\n\nmutation MarkArticleRead($id: ID!) {\n markArticleRead(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkArticleUnread($id: ID!) {\n markArticleUnread(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n }\n}\n\nmutation MarkFeedRead($id: ID!) {\n markFeedRead(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}\n\nmutation MarkFeedUnread($id: ID!) {\n markFeedUnread(id: $id) {\n id\n url\n title\n fetchedAt\n }\n}"];
+/**
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
+ */
+export function graphql(source: "query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}"): (typeof documents)["query GetFeeds {\n feeds {\n id\n url\n title\n fetchedAt\n articles {\n id\n isRead\n }\n }\n}\n\nquery GetUnreadArticles {\n unreadArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetReadArticles {\n readArticles {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}\n\nquery GetFeed($id: ID!) {\n feed(id: $id) {\n id\n url\n title\n fetchedAt\n articles {\n id\n guid\n title\n url\n isRead\n }\n }\n}\n\nquery GetArticle($id: ID!) {\n article(id: $id) {\n id\n feedId\n guid\n title\n url\n isRead\n feed {\n id\n title\n }\n }\n}"];
+
+export function graphql(source: string) {
+ return (documents as any)[source] ?? {};
+}
+
+export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never; \ No newline at end of file
diff --git a/frontend/src/graphql/generated/graphql.ts b/frontend/src/graphql/generated/graphql.ts
new file mode 100644
index 0000000..a54f1b6
--- /dev/null
+++ b/frontend/src/graphql/generated/graphql.ts
@@ -0,0 +1,212 @@
+/* eslint-disable */
+import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
+export type Maybe<T> = T | null;
+export type InputMaybe<T> = Maybe<T>;
+export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
+export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
+export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
+export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
+export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
+/** All built-in and custom scalars, mapped to their actual values */
+export type Scalars = {
+ ID: { input: string; output: string; }
+ String: { input: string; output: string; }
+ Boolean: { input: boolean; output: boolean; }
+ Int: { input: number; output: number; }
+ Float: { input: number; output: number; }
+ DateTime: { input: string; output: string; }
+};
+
+/** Represents an individual article/post from a feed */
+export type Article = {
+ /** The feed this article belongs to */
+ feed: Feed;
+ /** ID of the feed this article belongs to */
+ feedId: Scalars['ID']['output'];
+ /** GUID from the RSS/Atom feed (unique identifier from feed) */
+ guid: Scalars['String']['output'];
+ /** Unique identifier for the article */
+ id: Scalars['ID']['output'];
+ /** Whether the article has been marked as read */
+ isRead: Scalars['Boolean']['output'];
+ /** Title of the article */
+ title: Scalars['String']['output'];
+ /** URL/link to the original article */
+ url: Scalars['String']['output'];
+};
+
+/** Represents a feed subscription in the system */
+export type Feed = {
+ /** Articles belonging to this feed */
+ articles: Array<Article>;
+ /** Timestamp when the feed was last fetched */
+ fetchedAt: Scalars['DateTime']['output'];
+ /** Unique identifier for the feed */
+ id: Scalars['ID']['output'];
+ /** Title of the feed (extracted from feed metadata) */
+ title: Scalars['String']['output'];
+ /** URL of the RSS/Atom feed */
+ url: Scalars['String']['output'];
+};
+
+/** Root mutation type for modifying data */
+export type Mutation = {
+ /** Add a new feed subscription */
+ addFeed: Feed;
+ /** Mark an article as read */
+ markArticleRead: Article;
+ /** Mark an article as unread */
+ markArticleUnread: Article;
+ /** Mark all articles in a feed as read */
+ markFeedRead: Feed;
+ /** Mark all articles in a feed as unread */
+ markFeedUnread: Feed;
+ /** Remove a feed subscription and all its articles */
+ removeFeed: Scalars['Boolean']['output'];
+};
+
+
+/** Root mutation type for modifying data */
+export type MutationAddFeedArgs = {
+ url: Scalars['String']['input'];
+};
+
+
+/** Root mutation type for modifying data */
+export type MutationMarkArticleReadArgs = {
+ id: Scalars['ID']['input'];
+};
+
+
+/** Root mutation type for modifying data */
+export type MutationMarkArticleUnreadArgs = {
+ id: Scalars['ID']['input'];
+};
+
+
+/** Root mutation type for modifying data */
+export type MutationMarkFeedReadArgs = {
+ id: Scalars['ID']['input'];
+};
+
+
+/** Root mutation type for modifying data */
+export type MutationMarkFeedUnreadArgs = {
+ id: Scalars['ID']['input'];
+};
+
+
+/** Root mutation type for modifying data */
+export type MutationRemoveFeedArgs = {
+ id: Scalars['ID']['input'];
+};
+
+/** Root query type for reading data */
+export type Query = {
+ /** Get a specific article by ID */
+ article?: Maybe<Article>;
+ /** Get a specific feed by ID */
+ feed?: Maybe<Feed>;
+ /** Get all feeds with their metadata */
+ feeds: Array<Feed>;
+ /** Get all read articles across all feeds */
+ readArticles: Array<Article>;
+ /** Get all unread articles across all feeds */
+ unreadArticles: Array<Article>;
+};
+
+
+/** Root query type for reading data */
+export type QueryArticleArgs = {
+ id: Scalars['ID']['input'];
+};
+
+
+/** Root query type for reading data */
+export type QueryFeedArgs = {
+ id: Scalars['ID']['input'];
+};
+
+export type AddFeedMutationVariables = Exact<{
+ url: Scalars['String']['input'];
+}>;
+
+
+export type AddFeedMutation = { addFeed: { id: string, url: string, title: string, fetchedAt: string } };
+
+export type RemoveFeedMutationVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type RemoveFeedMutation = { removeFeed: boolean };
+
+export type MarkArticleReadMutationVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type MarkArticleReadMutation = { markArticleRead: { id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean } };
+
+export type MarkArticleUnreadMutationVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type MarkArticleUnreadMutation = { markArticleUnread: { id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean } };
+
+export type MarkFeedReadMutationVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type MarkFeedReadMutation = { markFeedRead: { id: string, url: string, title: string, fetchedAt: string } };
+
+export type MarkFeedUnreadMutationVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type MarkFeedUnreadMutation = { markFeedUnread: { id: string, url: string, title: string, fetchedAt: string } };
+
+export type GetFeedsQueryVariables = Exact<{ [key: string]: never; }>;
+
+
+export type GetFeedsQuery = { feeds: Array<{ id: string, url: string, title: string, fetchedAt: string, articles: Array<{ id: string, isRead: boolean }> }> };
+
+export type GetUnreadArticlesQueryVariables = Exact<{ [key: string]: never; }>;
+
+
+export type GetUnreadArticlesQuery = { unreadArticles: Array<{ id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean, feed: { id: string, title: string } }> };
+
+export type GetReadArticlesQueryVariables = Exact<{ [key: string]: never; }>;
+
+
+export type GetReadArticlesQuery = { readArticles: Array<{ id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean, feed: { id: string, title: string } }> };
+
+export type GetFeedQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type GetFeedQuery = { feed?: { id: string, url: string, title: string, fetchedAt: string, articles: Array<{ id: string, guid: string, title: string, url: string, isRead: boolean }> } | null };
+
+export type GetArticleQueryVariables = Exact<{
+ id: Scalars['ID']['input'];
+}>;
+
+
+export type GetArticleQuery = { article?: { id: string, feedId: string, guid: string, title: string, url: string, isRead: boolean, feed: { id: string, title: string } } | null };
+
+
+export const AddFeedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AddFeed"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"url"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"addFeed"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"url"},"value":{"kind":"Variable","name":{"kind":"Name","value":"url"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}}]}}]}}]} as unknown as DocumentNode<AddFeedMutation, AddFeedMutationVariables>;
+export const RemoveFeedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemoveFeed"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"removeFeed"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}]}}]} as unknown as DocumentNode<RemoveFeedMutation, RemoveFeedMutationVariables>;
+export const MarkArticleReadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkArticleRead"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markArticleRead"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]} as unknown as DocumentNode<MarkArticleReadMutation, MarkArticleReadMutationVariables>;
+export const MarkArticleUnreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkArticleUnread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markArticleUnread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]} as unknown as DocumentNode<MarkArticleUnreadMutation, MarkArticleUnreadMutationVariables>;
+export const MarkFeedReadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkFeedRead"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markFeedRead"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}}]}}]}}]} as unknown as DocumentNode<MarkFeedReadMutation, MarkFeedReadMutationVariables>;
+export const MarkFeedUnreadDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"MarkFeedUnread"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"markFeedUnread"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}}]}}]}}]} as unknown as DocumentNode<MarkFeedUnreadMutation, MarkFeedUnreadMutationVariables>;
+export const GetFeedsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFeeds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feeds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}},{"kind":"Field","name":{"kind":"Name","value":"articles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]}}]} as unknown as DocumentNode<GetFeedsQuery, GetFeedsQueryVariables>;
+export const GetUnreadArticlesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetUnreadArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unreadArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode<GetUnreadArticlesQuery, GetUnreadArticlesQueryVariables>;
+export const GetReadArticlesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReadArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"readArticles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode<GetReadArticlesQuery, GetReadArticlesQueryVariables>;
+export const GetFeedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetFeed"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feed"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"fetchedAt"}},{"kind":"Field","name":{"kind":"Name","value":"articles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}}]}}]}}]}}]} as unknown as DocumentNode<GetFeedQuery, GetFeedQueryVariables>;
+export const GetArticleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetArticle"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"article"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"feedId"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"isRead"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode<GetArticleQuery, GetArticleQueryVariables>; \ No newline at end of file
diff --git a/frontend/src/graphql/generated/index.ts b/frontend/src/graphql/generated/index.ts
new file mode 100644
index 0000000..f515991
--- /dev/null
+++ b/frontend/src/graphql/generated/index.ts
@@ -0,0 +1,2 @@
+export * from "./fragment-masking";
+export * from "./gql"; \ No newline at end of file
diff --git a/frontend/src/graphql/mutations.graphql b/frontend/src/graphql/mutations.graphql
new file mode 100644
index 0000000..8bcbcec
--- /dev/null
+++ b/frontend/src/graphql/mutations.graphql
@@ -0,0 +1,52 @@
+mutation AddFeed($url: String!) {
+ addFeed(url: $url) {
+ id
+ url
+ title
+ fetchedAt
+ }
+}
+
+mutation RemoveFeed($id: ID!) {
+ removeFeed(id: $id)
+}
+
+mutation MarkArticleRead($id: ID!) {
+ markArticleRead(id: $id) {
+ id
+ feedId
+ guid
+ title
+ url
+ isRead
+ }
+}
+
+mutation MarkArticleUnread($id: ID!) {
+ markArticleUnread(id: $id) {
+ id
+ feedId
+ guid
+ title
+ url
+ isRead
+ }
+}
+
+mutation MarkFeedRead($id: ID!) {
+ markFeedRead(id: $id) {
+ id
+ url
+ title
+ fetchedAt
+ }
+}
+
+mutation MarkFeedUnread($id: ID!) {
+ markFeedUnread(id: $id) {
+ id
+ url
+ title
+ fetchedAt
+ }
+}
diff --git a/frontend/src/graphql/queries.graphql b/frontend/src/graphql/queries.graphql
new file mode 100644
index 0000000..af4ac01
--- /dev/null
+++ b/frontend/src/graphql/queries.graphql
@@ -0,0 +1,73 @@
+query GetFeeds {
+ feeds {
+ id
+ url
+ title
+ fetchedAt
+ articles {
+ id
+ isRead
+ }
+ }
+}
+
+query GetUnreadArticles {
+ unreadArticles {
+ id
+ feedId
+ guid
+ title
+ url
+ isRead
+ feed {
+ id
+ title
+ }
+ }
+}
+
+query GetReadArticles {
+ readArticles {
+ id
+ feedId
+ guid
+ title
+ url
+ isRead
+ feed {
+ id
+ title
+ }
+ }
+}
+
+query GetFeed($id: ID!) {
+ feed(id: $id) {
+ id
+ url
+ title
+ fetchedAt
+ articles {
+ id
+ guid
+ title
+ url
+ isRead
+ }
+ }
+}
+
+query GetArticle($id: ID!) {
+ article(id: $id) {
+ id
+ feedId
+ guid
+ title
+ url
+ isRead
+ feed {
+ id
+ title
+ }
+ }
+}