Top Hacker News Story Aggregator Using Next.js, Resend and Cron Atlas

Peter Mbanugo - Jan 2 - - Dev Community

Hacker News is a social news website focusing on technology and entrepreneurship. You probably visit the website occasionally to stay informed about new or trending technologies or join discussions on one of the trending topics. As an entrepreneur, I like to check out what's trending in the Show HN section. However, I don't want to keep scrolling the page every day. I'd like a system that sends me a summary of the top stories.

In this blog post, I'm going to show you how to leverage Next.js, Resend, and Cron Atlas to create a cron job that collects the top 5 stories, and emails them to me every day.

Create The Next.js API Route

We're going to use Next.js API routes to implement a public API that will run as a cron job. This API route will fetch the top stories from the Hacker News API and email them to the specified email address.

Assuming we already have a Next.js app, we're going to add a new API route by creating a new file api/hacker-news.ts with the content below into it.

import { NextResponse } from "next/server";

export const config = {
  runtime: "edge",
};

type Story = {
  by: string;
  descendants: number;
  id: number;
  kids: number[];
  score: number;
  time: number;
  title: string;
  type: "story" | "job" | "poll" | "comment" | "pollopt";
  url: string;
};

export default async function handler() {
  try {
    const topStoriesUrl =
      "https://hacker-news.firebaseio.com/v0/topstories.json";

    // Fetch the top stories IDs
    const response = await fetch(topStoriesUrl);
    const storyIds = (await response.json()) as number[];
    const top5StoryIds = storyIds.slice(0, 5);

    // Fetch the story details for each top story in parallel
    const storyDetailsPromises = top5StoryIds.map((id) =>
      fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`).then(
        (res) => res.json() as Promise<Story>
      )
    );

    const topStories = await Promise.all(storyDetailsPromises);
    await sendEmail(topStories);

    return new NextResponse("Done");
  } catch (error) {
    return new NextResponse("Error", { status: 500 });
  }
}
Enter fullscreen mode Exit fullscreen mode

The code above gets the top stories ID from https://hacker-news.firebaseio.com/v0/topstories.json , then picks 5 IDs and retries the details for each of them. In the end, it calls the sendEmail() functions which take care of sending the email.

Sending Email with Resend

Resend is a transactional email service with a simple-to-use API. Sign up for an account at resend.com and retrieve your API key.

Next, we're going to implement the sendEmail() function used in the API route handler. Copy the function below and paste into the hacker-news.ts route file.

async function sendEmail(stories: Story[]) {
  const emailHtml = `<html>
    <body>
      <h1>Top 5 Hacker News Stories</h1>
      <ol>  
        ${stories
          .map((story) => `<li><a href="${story.url}">${story.title}</a></li>`)
          .join("")}
      </ol>
    </body>
  </html>`;

  const emailUrl = `https://api.resend.com/emails`;
  const emailHeaders = {
    Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
    "Content-Type": "application/json",
  };
  const emailBody = JSON.stringify({
    to: process.env.EMAIL_RECIPIENT,
    from: process.env.EMAIL_SENDER,
    subject: "Top 5 Hacker News Stories Roundup",
    html: emailHtml,
  });

  const emailResponse = fetch(emailUrl, {
    method: "POST",
    headers: emailHeaders,
    body: emailBody,
  }).then((res) => {
    if (res.ok) {
      console.log("Email sent successfully");
      return res.json();
    }
    throw new Error("Error sending email");
  });

  return emailResponse;
}
Enter fullscreen mode Exit fullscreen mode

With that done, you can deploy the app to wherever you prefer to host your Next.js app. For the code above to work, ensure you have the following environment variables.

  • RESEND_API_KEY: You can find your Resend API key in their dashboard.

  • EMAIL_RECIPIENT: The email to send to.

  • EMAIL_SENDER: The email to use as the sender. You can use onboarding@resend.dev if you haven't set up an email domain yet.

Next up, we will set up a job to run once a day.

Job Scheduling Using Cron Atlas

We will use Cron Atlas to schedule a job that calls the API route we created. Cron Atlas is an open-source job scheduling platform for the serverless era. We can create a job with flexible scheduling using cron expressions or a calendar-like scheduling format.

To set up a job on Cron Atlas, sign up for an account at cronatlas.dev. When you're logged in, click the New Cron Job button to take you to the job creation page.

Create Job Schedule

On this page, you'll have to fill out the fields to create the job. Following what's in the screenshot above, I specified a name for the job and gave it the URL to my Hacker News API route. I specified a schedule of 1 day, which means that Cron Atlas will call that endpoint once a day.

After the job is created, Cron Atlas will run that job at the specified interval and we can rest assured we don't miss the top Hacker News stories.

That's A Wrap

Wow! Isn't it awesome to easily implement a cron job using Next.js API routes and Cron Atlas? Top it off with how easy it is to send email using Resend.

To close off, check out Cron Atlas on GitHub and support us by giving it a star and also sharing it with your friends.

Cron Atlas GitHub Repo

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .