How to make a chatbot for the Reddit API

rafalzawadzki

Rafal Zawadzki

Posted on September 13, 2023

How to make a chatbot for the Reddit API

Hi there! Thought Iā€™d share this short guide on how I made a chatbot that lets users converse with the Reddit API. As a dev and Reddit fan, I wanted to try creating a new way of interacting with the platform. Here's how I did it using NextJS, Chatwith and OpenAPI!

Here is the source code if you want to jump right into it, and here is the final app: https://redditbot.chat

Project features

In summary, the project has:

  • A thin wrapper over the Reddit API to expose only useful endpoints and trim data payloads
  • An OpenAPI spec file in YAML for the Chatwith chatbot
  • A landing page with a chatbot iframe embed

Arguably most of the heavy lifting has been done by Chatwith. This project focuses on creating an API for the chatbot to interact with and setting up Chatwith to build the chatbot.

I used NextJS for this project because I also wanted to make a simple landing page - but you can easily create the API with eg. an Express backend.

Getting started

I used the plain NextJS 13 starter with the default configuration:

bunx create-next-app

You can see I'm using Bun which is the new JS all-in-one toolkit. You can also use npm or pnpm as a replacement in this project.

After creating the project, quickly check if it runs on localhost:

bun dev

Creating Reddit API wrapper

The essential component of this project is the Reddit API wrapper. It serves as a mediator between our chatbot and the Reddit API. Its role is to reduce the amount of data transferred and to simplify the API for the chatbot to understand.

Here's why the wrapper is important:

  • Use only selected endpoints: The Reddit API has a myriad of endpoints for various functionalities. Our chatbot only needs a subset of these. The wrapper makes sure to expose only the necessary ones.
  • Payload size: Reddit API responses contain a ton of information that we don't need. Because GPT-3.5 and GPT-4 from OpenAI accept limited tokens, the wrapper trims the API responses to contain only the data the bot will actually use.

API endpoints

Check the official Reddit API documentation for the full specification of all endpoints.

For now we'll add the following Reddit API endpoints in our wrapper:

  • /r/{subreddit}/{listing}.json: Fetches stories from a specific subreddit based on the type of listing (hot, new, rising, etc.)
  • /{listing}.json: Fetches stories from all subreddits based on the type of listing.
  • /r/{subreddit}/search.json: Allows us to search for a topic within any subreddit.
  • /search.json: Enables a search across all subreddits.
  • /r/{subreddit}/comments/{post_id}.json: Retrieves comments from a specific post within a subreddit.

Create API endpoint

Let's create a new NextJS route handler which will:

  • relay requests to Reddit API
  • trim the payloads and return a response

First, create a new file: /app/api/[...catchall]/route.ts

[...catchall] is the name of the directory and means that any path at /api/ will be handled by this route (so /api/search, /api/anything/goes etc). Learn more here.

In this file is a GET handler:

export const runtime = "edge";
export const revalidate = 10;

export async function GET(request: Request) {
  const urlObj = new URL(request.url);
  const path = urlObj.pathname.replace("/api/", "");
  const queryParams = urlObj.search;

  try {
    const redditResponse = await fetch(
      `https://api.reddit.com/${path}${queryParams}`
    );
    const redditData = await redditResponse.json();

    if (isPostListingResponse(redditData)) {
      // Handle posts
      const summaries = redditData.data.children.map(extractPostSummary);
      return NextResponse.json(summaries);
    } else if (isCommentsResponse(redditData)) {
      if (path.startsWith("r/") && path.includes("/comments/")) {
        // Handle comments
        const comments = redditData[1].data.children.map(extractCommentSummary);
        return NextResponse.json(comments);
      }
    } else {
      return new Response(null, {
        status: 400,
        statusText: "Invalid Reddit API response",
      });
    }
  } catch (error) {
    console.error(error);
    return new Response(null, {
      status: 500,
      statusText: "Failed to fetch data from Reddit",
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Let break it down step by step!

We'll use the Edge runtime for lowest latency and add short caching to not get rate limited by the Reddit API:

export const runtime = "edge";
export const revalidate = 10;
Enter fullscreen mode Exit fullscreen mode

Then, we clean up the request path so we can switch the host from our API to Reddit one:

const urlObj = new URL(request.url);
const path = urlObj.pathname.replace("/api/", "");
const queryParams = urlObj.search;
Enter fullscreen mode Exit fullscreen mode

Next, we make a request to Reddit API and parse the response JSON:

const redditResponse = await fetch(`https://api.reddit.com/${path}${queryParams}`);
const redditData = await redditResponse.json();
Enter fullscreen mode Exit fullscreen mode

Then we fiddle with data structures to recognize what kind of response we're dealing with so we can trim it and return back to the client:

if (isPostListingResponse(redditData)) {
  // Handle posts
  const summaries = redditData.data.children.map(extractPostSummary);
  return NextResponse.json(summaries);
} else if (isCommentsResponse(redditData)) {
  if (path.startsWith("r/") && path.includes("/comments/")) {
    // Handle comments
    const comments = redditData[1].data.children.map(extractCommentSummary);
    return NextResponse.json(comments);
  }
} else {
  return new Response(null, {
    status: 400,
    statusText: "Invalid Reddit API response",
  });
}
Enter fullscreen mode Exit fullscreen mode

You can see the full source code for the isPostListingResponse, isCommentsResponse, extractPostSummary and extractCommentSummary functions in the repository. The first two functions just check the existence and types of JSON elements, and the latter two map large objects returned by Reddit API into simplified ones that contain post title, author name etc.

OpenAPI specification

To allow our chatbot to talk to our API, we'll need to describe it using the OpenAPI specification (as per Chatwith requirements).

Let's start by creating a YAML file in the static assets directory: /public/openapi.yml. This way the file will be accessible to Chatwith after deployment like this: http://redditbot.chat/openapi.yml

Here's what we'll put in the file:

openapi: 3.0.0
info:
  title: Simple Reddit API
  version: 1.0.0
  description: An API specification for a simple Reddit client. Using this API you can respond to user queries by searching any subreddit or listing type.
servers:
  - url: https://redditbot.chat/api/
paths:
  /r/{subreddit}/{listing}.json:
    get:
      summary: Fetch stories from a specific subreddit by listing type
      parameters:
        - name: subreddit
          in: path
          required: true
          schema:
            type: string
        - name: listing
          in: path
          required: true
          schema:
            type: string
            enum:
              - hot
              - new
              - rising
              - top
              - controversial
        - name: limit
          in: query
          schema:
            type: integer
      responses:
        "200":
          description: A JSON object containing stories

[... trimmed for brevity]
Enter fullscreen mode Exit fullscreen mode

You can see the whole specification file content here. Make sure to stick to YAML whitespace formatting! Wrong tabs or spaces here and there may result in an incorrect spec file.

The important bits

Make sure to put the final path to your API route after its deployed. In my case, the project is deployed at https://redditbot.chat so the path to API wrapper is https://redditbot.chat/api/:

servers:
  - url: https://redditbot.chat/api/
Enter fullscreen mode Exit fullscreen mode

Document all endpoints the chatbot can use, along with short description and parameters.

paths:
  /r/{subreddit}/{listing}.json:
...
Enter fullscreen mode Exit fullscreen mode

The OpenAPI specification does not need to be very extensive and detailed, but it must be descriptive enough for the chatbot to understand how to make a correct request.

At this point we are over the hump, the hardest part is behind! Let's test the new endpoints by calling in the browser eg.:

http://localhost:3000/api/search.json?q=cat

This should return a JSON response with a bunch of cat posts! šŸ˜»

Creating the chatbot

The final step is creating a chatbot in Chatwith to get an embed. I'm using the free account. I followed this guide. Here are the steps:

Main steps

  1. Create a free Chatwith account here
  2. During onboarding pick the 'Chat with API' option
  3. Click 'Install' on the 'API' card
  4. Enter our API details in the popup
  5. Finish the onboarding flow and go to the dashboard to add a system prompt and customize the chatbot

Installing the API Action

Here's the screenshot of how installing the action popup looks like. You should put your OpenAPI spec file url and API urls (but not the localhost! must be the public url after deployment on eg Vercel).

Screenshot of Chatwith popup for adding API action

Adding system prompt

To give the chatbot instructions how to use the API, we will fill in the 'System Prompt' field in the 'Settings' section in the dashboard.

Here is the prompt:

You are Reddit AI assistant. You will respond to user query and using the provided API search posts in any subreddit and also by categories like hot, newest etc. You will provide a summary of listings and comments in an easy & digestible way. You are flexible and can understand the query so users can ask about news, fun posts, specialized subreddits etc. You will take the user query and try to make the best API request to find an answer to it. If a post has an image attached, you will show a thumbnail in markdown. Always provide post permalinks in the answer.

You can extend the prompt to add more character to the chatbot, eg. make it use emojis or funny language šŸ˜ø

Testing the chatbot

We're almost done! The chatbot should be working now. Let's go to 'Preview' section of the dashboard to test the chatbot. You can start by asking a question like Summarize hottest posts!

Fin

Hopefully at this point you have a cool chatbot that can talk to an API šŸ‘ If something didn't work or you have questions feel free to ping me in the comments!

As next steps, you can add more actions to your chatbot (ie. add more API endpoints for it to use) and customize how it looks in the Chatwith dashboard. After updating the API make sure to save the action in Chatwith again so the chatbot knows about the changes.

Thanks for reading!

šŸ’– šŸ’Ŗ šŸ™… šŸš©
rafalzawadzki
Rafal Zawadzki

Posted on September 13, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related