Hashnode Loader with Astro 5
Create Custom Loader for new Astro Content Collection
Install
graphql-request
npm install graphql-request yarn add graphql-request pnpm add graphql-request
Create file
src/loaders/hashnode/schemas.ts
withimport { z } from "astro/zod"; export const PostSchema = z.object({ author: z.object({ name: z.string(), profilePicture: z.string(), }), publishedAt: z.string(), title: z.string(), subtitle: z.string(), brief: z.string(), slug: z.string(), readTimeInMinutes: z.number(), content: z.object({ html: z.string(), }), tags: z.array(z.object({ name: z.string(), slug: z.string(), })), coverImage: z.object({ url: z.string(), }), }) export const PostsDataSchema = z.object({ publication: z.object({ title: z.string(), posts: z.object({ pageInfo: z.object({ hasNextPage: z.boolean(), endCursor: z.string(), }), edges: z.array(z.object({ node: PostSchema, })), }), }), }) export const PostDataSchema = z.object({ publication: z.object({ title: z.string(), post: PostSchema, }), }) export type Post = z.infer<typeof PostSchema> export type PostsData = z.infer<typeof PostsDataSchema> export type PostData = z.infer<typeof PostDataSchema>
Create file
src/loaders/hashnode/queries.ts
withimport { gql, GraphQLClient } from 'graphql-request'; import type { PostsData, PostData } from './schemas'; const getClient = () => new GraphQLClient('https://gql.hashnode.com'); export const getPosts = async (myHashnodeURL:string) => { const client = getClient(); const allPosts = await client.request<PostsData>( gql` query allPosts { publication(host: "${myHashnodeURL}") { title posts(first: 20) { pageInfo{ hasNextPage endCursor } edges { node { author{ name profilePicture } title subtitle brief slug coverImage { url } tags { name slug } publishedAt readTimeInMinutes } } } } } `, ); return allPosts; };
Create file
src/loaders/hashnode/loaders.ts
withimport { PostSchema } from './schemas'; import type { Loader } from 'astro/loaders'; import { getPosts } from './queries'; export interface HashnodePostsLoaderOptions { myHashnodeURL: string; } export function hashnodePostsLoader({ myHashnodeURL }: HashnodePostsLoaderOptions): Loader { return { name: 'hasnode-posts-loader', load: async ({ logger, parseData, store }) => { logger.info(`Loading posts from ${myHashnodeURL}`); const result = await getPosts(myHashnodeURL); for (const post of result.publication.posts.edges) { const data = post.node; store.set({ id: data.slug, data }); } logger.info(`Loaded ${result.publication.posts.edges.length} posts from ${myHashnodeURL}`); }, schema: () => PostSchema, }; }
Create file
src/content/config.ts
withimport { defineCollection } from 'astro:content' import { hashnodePostsLoader } from '../loaders/hasnode/loaders'; const myHashnodeURL = 'gelinjo.hashnode.dev' const posts = defineCollection({ loader: hashnodePostsLoader({myHashnodeURL}) }); export const collections = { posts }
Display on your Astro page like
--- import { getCollection } from 'astro:content'; const posts = await getCollection('posts'); --- posts.map((post) => ( <div> <h2>{post.data.title}</h2> <p>{post.data.brief}</p> <img src={post.data.coverImage.url} alt={post.data.title} /> <a href={`https://gelinjo.hashnode.dev/${post.data.slug}`}>Read more</a> </div> ))