Available for new projects in 2026 - Available 2026 - Get in touch

Hashnode Loader with Astro 5

Create Custom Loader for new Astro Content Collection

Hashnode Loader with Astro  5
  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

Docs Hashnode &#38; Astro
Astro Community Loaders for Astro Content Layer | Astro
Docs Content collections

Want to go further?

If you're looking to improve your Developer Experience, build AI Frameworks, or optimize your CI/CD pipelines with Nx, feel free to reach out — I'm always happy to share real-world setups and practical tips.

Jonathan Gelin

Build on Foundations,
Scale with AI.

I design AI-powered workflows and engineering foundations that make teams faster, happier, and more consistent.