Hashnode Loader with Astro 5

Jonathan GelinJonathan Gelin
2 min read
  1. Install graphql-request

     npm install graphql-request
     yarn add graphql-request
     pnpm add graphql-request
    
  2. Create file src/loaders/hashnode/schemas.ts with

     import { 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>
    
  3. Create file src/loaders/hashnode/queries.ts with

     import { 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;
     };
    
  4. Create file src/loaders/hashnode/loaders.ts with

     import { 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,
       };
     }
    
  5. Create file src/content/config.ts with

     import { 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 }
    
  6. 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>
     ))
    

Resources

0
Subscribe to my newsletter

Read articles from Jonathan Gelin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jonathan Gelin
Jonathan Gelin

Who am I? Whether it's as a Software Engineer, Tech Lead, or Architect, if it involves software development, I'm in! My journey began in the realm of Java development, but I fully transitioned into the universe of JavaScript/TypeScript and its exciting toolset. I support companies through their software development cycle challenges by utilizing Nx monorepos, micro frontends, robust testing strategies, and a touch of Extreme Programming philosophy. Every day for me is like waiting for the next episode of my favorite series—filled with learning, sharing, and growing together. Indeed, I'm as passionate about coaching and sharing knowledge as I am about coding. I am the father of two incredible boys, and I am endlessly grateful to my wife for supporting my passion every day.