diff --git a/client/src/app.d.ts b/client/src/app.d.ts index f5ebc621..a2f9bf99 100644 --- a/client/src/app.d.ts +++ b/client/src/app.d.ts @@ -1,7 +1,10 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces -import type { CurrentUserFragmentFragment } from '$lib/graphql/schema'; +import type { + CurrentUserFragmentFragment, + PostEdge, +} from '$lib/graphql/schema'; declare global { namespace App { @@ -11,6 +14,7 @@ declare global { interface PageData { accessToken?: string | null; user?: CurrentUserFragmentFragment | null; + posts?: PostEdge[] | null; } interface Locals { diff --git a/client/src/lib/graphql/schema.ts b/client/src/lib/graphql/schema.ts index c280eb96..949c66ad 100644 --- a/client/src/lib/graphql/schema.ts +++ b/client/src/lib/graphql/schema.ts @@ -53,6 +53,19 @@ export type MutationRootUserRegisterArgs = { input: UserRegisterInput; }; +/** Information about pagination in a connection */ +export type PageInfo = { + __typename?: 'PageInfo'; + /** When paginating forwards, the cursor to continue. */ + endCursor?: Maybe; + /** When paginating forwards, are there more items? */ + hasNextPage: Scalars['Boolean']['output']; + /** When paginating backwards, are there more items? */ + hasPreviousPage: Scalars['Boolean']['output']; + /** When paginating backwards, the cursor to continue. */ + startCursor?: Maybe; +}; + export type Post = { __typename?: 'Post'; authorId: Scalars['Pxid']['output']; @@ -65,6 +78,16 @@ export type Post = { updatedAt: Scalars['DateTime']['output']; }; +export type PostConnection = { + __typename?: 'PostConnection'; + /** A list of edges. */ + edges: Array; + /** A list of nodes. */ + nodes: Array; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + export type PostCreate = { __typename?: 'PostCreate'; error?: Maybe; @@ -77,6 +100,15 @@ export type PostCreateInput = { title: Scalars['String']['input']; }; +/** An edge in a connection. */ +export type PostEdge = { + __typename?: 'PostEdge'; + /** A cursor for use in pagination */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge */ + node: Post; +}; + export type PostError = { __typename?: 'PostError'; code: PostErrorCode; @@ -92,6 +124,15 @@ export enum PostErrorCode { export type QueryRoot = { __typename?: 'QueryRoot'; me: Me; + posts: PostConnection; +}; + + +export type QueryRootPostsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; }; export type TokenCreate = { @@ -146,6 +187,13 @@ export type GetCurrentUserQuery = { __typename?: 'QueryRoot', me: { __typename?: export type CurrentUserFragment = { __typename?: 'User', id: any, name: string, surname: string, email: string, username: string, createdAt: any, updatedAt: any }; +export type GetPostsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetPostsQuery = { __typename?: 'QueryRoot', posts: { __typename?: 'PostConnection', edges: Array<{ __typename?: 'PostEdge', node: { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content: string, createdAt: any, updatedAt: any } }>, pageInfo: { __typename?: 'PageInfo', hasPreviousPage: boolean, hasNextPage: boolean, startCursor?: string | null, endCursor?: string | null }, nodes: Array<{ __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content: string, createdAt: any, updatedAt: any }> } }; + +export type CurrentPostFragment = { __typename?: 'Post', id: any, authorId: any, parentId?: any | null, head: boolean, title: string, content: string, createdAt: any, updatedAt: any }; + export type PostCreateMutationVariables = Exact<{ input: PostCreateInput; }>; @@ -179,6 +227,18 @@ export const CurrentUserFragmentDoc = gql` updatedAt } `; +export const CurrentPostFragmentDoc = gql` + fragment CurrentPost on Post { + id + authorId + parentId + head + title + content + createdAt + updatedAt +} + `; export const GetCurrentUserDocument = gql` query GetCurrentUser { me { @@ -188,6 +248,26 @@ export const GetCurrentUserDocument = gql` } } ${CurrentUserFragmentDoc}`; +export const GetPostsDocument = gql` + query GetPosts { + posts { + edges { + node { + ...CurrentPost + } + } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + nodes { + ...CurrentPost + } + } +} + ${CurrentPostFragmentDoc}`; export const PostCreateDocument = gql` mutation PostCreate($input: PostCreateInput!) { postCreate(input: $input) { diff --git a/client/src/routes/(app)/+page.svelte b/client/src/routes/(app)/+page.svelte index 51626a89..496f939f 100644 --- a/client/src/routes/(app)/+page.svelte +++ b/client/src/routes/(app)/+page.svelte @@ -1,9 +1,13 @@
Welcome back {$page.data.user.name}! + {#each $page.data.posts || [] as { node: post }} + + {/each}
diff --git a/client/src/routes/(app)/+page.ts b/client/src/routes/(app)/+page.ts new file mode 100644 index 00000000..686c8ec0 --- /dev/null +++ b/client/src/routes/(app)/+page.ts @@ -0,0 +1,10 @@ +import type { PostEdge } from '$lib/graphql/schema.js'; + +/** @type {import('./$types').PageLoad} */ +export async function load({ fetch }) { + const response = await fetch('/get-posts'); + const data = await response.json(); + return { + posts: data as PostEdge[], + }; +} diff --git a/client/src/routes/(app)/components/Post.svelte b/client/src/routes/(app)/components/Post.svelte index 40a4b167..b467409c 100644 --- a/client/src/routes/(app)/components/Post.svelte +++ b/client/src/routes/(app)/components/Post.svelte @@ -1,33 +1,22 @@
- +
- {post.user.username} +

{post.title}

{new Date(post.createdAt).toLocaleDateString()}
-

{post.title}

-

{post.content}

+

{post.content}

diff --git a/client/src/routes/(app)/get-posts/+server.ts b/client/src/routes/(app)/get-posts/+server.ts new file mode 100644 index 00000000..a937b91c --- /dev/null +++ b/client/src/routes/(app)/get-posts/+server.ts @@ -0,0 +1,43 @@ +import { GetPostsDocument, type PostConnection } from '$lib/graphql/schema'; +import { Client, cacheExchange, createClient, fetchExchange } from '@urql/core'; + +async function getPosts(urqlClient: Client) { + const response = await urqlClient + .query(GetPostsDocument, {}, { requestPolicy: 'network-only' }) + .toPromise(); + + if (response?.error || response?.data?.posts?.error) { + if (response?.data?.posts?.error) { + const error = response?.data?.posts?.error; + + throw new Error(error); + } + + throw response?.error; + } + + return (response?.data?.posts as PostConnection).edges; +} + +export const GET = async ({ request }: { request: Request }) => { + try { + const urqlClient = createClient({ + url: import.meta.env.VITE_GRAPHQL_URL, + exchanges: [cacheExchange, fetchExchange], + }); + + const posts = await getPosts(urqlClient); + + return new Response(JSON.stringify(posts), { status: 200 }); + } catch (err) { + console.log(err); + + return new Response( + JSON.stringify({ + message: 'Internal Server Error', + error: (err as { message: string })?.message, + }), + { status: 500 }, + ); + } +}; diff --git a/client/src/routes/(app)/graphql/GetPosts.gql b/client/src/routes/(app)/graphql/GetPosts.gql new file mode 100644 index 00000000..6b7fda45 --- /dev/null +++ b/client/src/routes/(app)/graphql/GetPosts.gql @@ -0,0 +1,29 @@ +query GetPosts { + posts { + edges { + node { + ...CurrentPost + } + } + pageInfo { + hasPreviousPage + hasNextPage + startCursor + endCursor + } + nodes { + ...CurrentPost + } + } +} + +fragment CurrentPost on Post { + id + authorId + parentId + head + title + content + createdAt + updatedAt +} \ No newline at end of file