npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

reddit-explorer

v1.5.0-alpha

Published

Simple reddit client that allows to fetch reddit names and content from any subreddit, without any account authentication.

Downloads

103

Readme

Maintainability Rating Security Rating Bugs Vulnerabilities Duplicated Lines (%) Reliability Rating Quality Gate Status Technical Debt Coverage Lines of Code Code Smells

reddit-explorer

reddit-explorer is a reddit client that allows you to fetch subreddit content easily. It only has 2 dependencies (typescript & axios).

To see what has been done on each version, you can consult the CHANGELOG.

Installation

Command line

npm install reddit-explorer

or

yarn add reddit-explorer

TODO: get subreddit media documentation


Create a dev application on Reddit (if you do not have already one)

You have to pass your app credentials, see how to generate some:

  1. Go to https://www.reddit.com/prefs/apps

  2. At the bottom of the page click "create another app...", fill all the fields and then click "create app". You now see your clientId and secret.

    credentials

API

For the moment reddit-explorer has 4 features:

  1. Authentication with clientId & secret & easily getting the access_token
  2. Getting similar subreddit names of a string
  3. Getting the content of one or multiple subreddit(s) (+ automatic pagination handling)
  4. Easily filter the posts of a response

I'm actively working on other features that are coming soon :)

Initializing the client

import { createRedditClient, SortingMethod, TimeRange, PostFilter, moreThanNComments } from "reddit-explorer"

const reddit = createRedditClient({
    clientId: "<clientId>",
    secret: "<secret>",
})

Getting the access token

You usually do not need this method, but can be useful if you want to see the authentication detail.

reddit.getAccessToken()

// {
//     "access_token": "-QFrCA5GkksABCEibAakz2fQbzUE62A",  <-- You can use this an put it in the next bearer token's request
//     "token_type": "bearer",
//     "device_id": "5f615d62-fa4c-11eb-b8bc-0242ac130003",
//     "expires_in": 86400,
//     "scope": "*"
// }

Getting subreddit names

reddit.getSubredditNames({ query: "photo" })
    .then((res) => console.log("photo", res))

// photo {
//    names: [
//       'photo',
//       'photoshopbattles',
//       'photography',
//       'photocritique',
//       'photoshop',
//       'PhotoshopRequest',
//       'PhotoshopTutorials',
//       'photographs',
//       'photomarket',
//       'Photography101'
//    ]
// }

Include adult subreddit names

There are three ways to achieve that:

  1. At the initialization of the client:
    import { createRedditClient } from "reddit-explorer"
      
    const reddit = createRedditClient({
       clientId: "<clientId>",
       secret: "<secret>",
       matureContent: true,
    })
  2. When calling the method:
    reddit.getSubredditNames({ query: "photo", include_over_18: true })
        .then((res) => console.log("photo", res))
  3. After the initialization of the client
    reddit.config.setMatureContent(true)

Getting the content of a subreddit

You have to pass a name and a SortingMethod. Sorting method can be: Hot, New, Random, Rising, Top or Controversial.

If the sorting method is Top or Controversial, you can also pass a TimeRange. Time range can be: Hour, Day, Week, Month, Year, All.

The package offers the type SubredditData which is the type of all the [Object] below (see at the end of the readme).

reddit
    .getSubreddit({
        name: "meme",
        sortMethod: SortingMethod.New,
    })
    .then((res) => console.log("meme", res))

// meme {
//     kind: 'Listing',
//     data: {
//     after: 't3_u9osas',
//     dist: 25,
//     modhash: '',
//     geo_filter: '',
//     children: [
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object], [Object], [Object],
//          [Object]
//       ], 
//     before: null
//    }
// }

// Example with a TimeRange:

reddit
    .getSubreddit({
       name: "meme",
       sortMethod: SortingMethod.Top,
       t: TimeRange.Day,
    })
    .then((res) => console.log("meme", res))

Filter the results of a response

If you want to keep only specific posts in the response, you can use postFilters when creating the client. This attribute accepts an array of functions, each one taking a SubredditData into parameter and returning a boolean, (see the type PostFilter). The package also comes with multiple PostFilterCreator's, which are basically functions that return a PostFilter, such as moreThanNComments(100) below:

const moreThan2Crossposts: PostFilter = (subredditData) => subredditData.num_crossposts > 2

const clientShowingOnlyPopularPosts = createRedditClient({
    clientId: secrets.clientId,
    secret: secrets.secret,
    postFilters: [moreThanNComments(100), moreThan2Crossposts],
})

Loading next results

The API can only load a max amount of 100 posts (the default is 25). To load the next posts, you have to pass the after param. You can find the value of the after in the previous response of the request you made, in response.after. It contains the name (SubredditData.name) of the last post in the previous call.

reddit
    .getSubreddit({
        name: "meme",
        sortMethod: SortingMethod.New,
        after: "t3_u9osas", // response.after of the previous call
    })
    .then((res) => console.log("meme", res))

Using iterators

If you do not want to memorize the after attribute of the last response yourself, you can let the package handle it for you, using getSubredditIterator:

const memeSubredditIterator = reddit.getSubredditIterator({
    name: "meme",
    sortMethod: SortingMethod.New,
    limit: 5,
})

const memeResults0To4 = await memeSubredditIterator.next()
const memeResults5To9 = await memeSubredditIterator.next()

console.log("memeResults0To4", memeResults0To4.value)
console.log("memeResults5To9", memeResults5To9.value)

// memeResults0To4 {
//     kind: 'Listing',
//         data: {
//         after: 't3_uaslor',
//             dist: 5,
//             modhash: '',
//             geo_filter: '',
//             children: [ [Object], [Object], [Object], [Object], [Object] ],
//             before: null
//     }
// }
// memeResults5To9 {
//     kind: 'Listing',
//         data: {
//         after: 't3_uas9sy',
//             dist: 5,
//             modhash: '',
//             geo_filter: '',
//             children: [ [Object], [Object], [Object], [Object], [Object] ],
//             before: null
//     }
// }

Getting the content of multiple subreddits

Instead of passing a string to the name argument, you can pass a string[] to it:

const res = await client.getSubreddit({
    sortMethod: SortingMethod.Top,
    name: ["news", "meme"],
})

Reddit simple client

createRedditSimpleClient allows to get only the essential. This client will return a modified response containing only:

  • kind
  • title
  • url
  • subreddit
  • thumbnail
  • permalink
  • link
  • createdAtUtc
  • createdAtLocal

For the moment, the simple client only supports getSubreddit and getSubredditNames.

const reddit = createRedditSimpleClient({
    clientId: "<clientId>",
    secret: "<clientSecret>",
})

const result = await reddit.getSubreddit({
    sortMethod: SortingMethod.Hot,
    name: "memes",
    fields: ["title", "url"],
})

In the above example, I picked only the fields that I needed, here is the response:

{
  before: null,
  after: 't3_1aihq82',
  data: [
    {
      title: 'r/Memes is looking for new moderators! Interested? Fill out our application!',
      url: 'https://docs.google.com/forms/d/e/1FAIpQLSfBlrL6LVOktwIdGubvbJ7REeh9vANiBTIpUecW63PHINQECg/viewform'
    },
    {
      title: 'Worst days in University',
      url: 'https://i.redd.it/u8hlua9lbkgc1.jpeg'
    },
    // some other results...
  ],
}

SubredditData type

This type offers the possibility to handle the response more easily (response.children[0]):

export type SubredditData = {
    approved_at_utc: any
    subreddit: string
    selftext: string
    author_fullname: string
    saved: boolean
    mod_reason_title: any
    gilded: number
    clicked: boolean
    title: string
    link_flair_richtext: any[]
    subreddit_name_prefixed: string
    hidden: boolean
    pwls: number
    link_flair_css_class: any
    downs: number
    thumbnail_height: number
    top_awarded_type: any
    hide_score: boolean
    name: string
    quarantine: boolean
    link_flair_text_color: string
    upvote_ratio: number
    author_flair_background_color: any
    subreddit_type: string
    ups: number
    total_awards_received: number
    media_embed: null | {
        content: IFrame
        width: number
        scrolling: boolean
        height: number
    }
    thumbnail_width: number
    author_flair_template_id: any
    is_original_content: boolean
    user_reports: any[]
    secure_media: null | {
        type: string
        oembed: {
            provider_url: string
            version: string
            title: string
            type: string
            thumbnail_width: number
            height: number
            width: number
            html: IFrame
            author_name: string
            provider_name: string
            thumbnail_url: string
            thumbnail_height: number
            author_url: string
        }
    }
    is_reddit_media_domain: boolean
    is_meta: boolean
    category: any
    secure_media_embed: null | {
        content: IFrame
        width: number
        scrolling: boolean
        media_domain_url: Url
        height: number
    }
    link_flair_text: string
    can_mod_post: boolean
    score: number
    approved_by: any
    is_created_from_ads_ui: boolean
    author_premium: boolean
    thumbnail: Url
    edited: boolean
    author_flair_css_class: any
    author_flair_richtext: any[]
    gildings: {
        gid_1: number
    }
    post_hint: string
    content_categories: any
    is_self: boolean
    mod_note: any
    crosspost_parent_list?: SubredditData[]
    created: number
    link_flair_type: string
    wls: number
    removed_by_category: any
    banned_by: any
    author_flair_type: string
    domain: Url
    allow_live_comments: boolean
    selftext_html: any
    likes: any
    suggested_sort: any
    banned_at_utc: any
    url_overridden_by_dest: Url
    view_count: any
    archived: boolean
    no_follow: boolean
    is_crosspostable: boolean
    pinned: boolean
    over_18: boolean
    preview: {
        images: [
            {
                source: {
                    url: Url
                    width: number
                    height: number
                }
                resolutions: {
                    url: Url
                    width: number
                    height: number
                }[]
                variants: {
                    obfuscated: {
                        source: {
                            url: Url
                            width: number
                            height: number
                        }
                        resolutions: {
                            url: Url
                            width: number
                            height: number
                        }[]
                    }
                    nsfw: {
                        source: {
                            url: Url
                            width: number
                            height: number
                        }
                        resolutions: {
                            url: Url
                            width: number
                            height: number
                        }[]
                    }
                }
                id: string
            }
        ]
        reddit_video_preview: {
            bitrate_kbps: number
            fallback_url: string
            height: number
            width: number
            scrubber_media_url: string
            dash_url: string
            duration: number
            hls_url: string
            is_gif: boolean
            transcoding_status: string
        }
        enabled: boolean
    }
    all_awardings: any[]
    awarders: any[]
    media_only: boolean
    can_gild: boolean
    spoiler: boolean
    locked: boolean
    author_flair_text: null
    treatment_tags: []
    visited: boolean
    removed_by: null
    num_reports: null
    distinguished: null
    subreddit_id: string
    mod_reason_by: null
    removal_reason: null
    link_flair_background_color: string
    id: string
    is_robot_indexable: boolean
    report_reasons: null
    author: string
    discussion_type: null
    num_comments: number
    send_replies: boolean
    whitelist_status: string
    contest_mode: boolean
    mod_reports: []
    author_patreon_flair: boolean
    crosspost_parent?: string
    author_flair_text_color: null
    permalink: string
    parent_whitelist_status: string
    stickied: boolean
    url: Url
    subreddit_subscribers: number
    created_utc: number
    num_crossposts: number
    media: null | {
        type: string
        oembed: {
            provider_url: Url
            version: string
            title: string
            type: string
            thumbnail_width: number
            height: number
            width: number
            html: IFrame
            author_name: string
            provider_name: string
            thumbnail_url: Url
            thumbnail_height: number
            author_url: Url
        }
    }
    is_video: boolean
    sr_detail: {
        default_set: boolean
        banner_img: string
        restrict_posting: boolean
        user_is_banned?: any
        free_form_reports: boolean
        community_icon?: any
        show_media: boolean
        description: string
        user_is_muted?: any
        display_name: string
        header_img: string
        title: string
        previous_names: any[]
        user_is_moderator?: any
        over_18: boolean
        icon_size: number[]
        primary_color: string
        icon_img: string
        icon_color: string
        submit_link_label: string
        header_size: number[]
        restrict_commenting: boolean
        subscribers: number
        submit_text_label: string
        link_flair_position: string
        display_name_prefixed: string
        key_color: string
        name: string
        created: number
        url: string
        quarantine: boolean
        created_utc: number
        banner_size: number[]
        user_is_contributor?: any
        accept_followers: boolean
        public_description: string
        link_flair_enabled: boolean
        disable_contributor_requests: boolean
        subreddit_type: string
        user_is_subscriber?: any
    }
}

Sources

Here you'll find the official documentation of the reddit official endpoints that this package uses:

  • Search reddit names: https://www.reddit.com/dev/api/#GET_api_search_reddit_names
  • Subreddit
    • controversial: https://www.reddit.com/dev/api/#GET_controversial
    • hot: https://www.reddit.com/dev/api/#GET_hot
    • new: https://www.reddit.com/dev/api/#GET_new
    • random: https://www.reddit.com/dev/api/#GET_random
    • rising: https://www.reddit.com/dev/api/#GET_rising
    • top: https://www.reddit.com/dev/api/#GET_top
    • sort: https://www.reddit.com/dev/api/#GET_{sort}