I have a grid of project tiles on my homepage that show dynamic stats at the bottom. It completes the design and makes these tiles a bit more interesting.

Being able to see the stats change over time is really motivating. Small, but needed push to keep working on these projects.

Looking for a way in...

NPM does not have any documentation about their private API that I could find. Snooping around network requests does not reveal any useful endpoints for grabbing this data because they are rendering each package page on the server.

After digging around the internet for a few minutes I found an article by Edoardo Scibona that explains how to use NPM's registry API endpoint to get the weekly downloads count for a package.

Getting the data

You can get the downloads count of a package published to NPM with a simple fetch to this endpoint:

https://api.npmjs.org/downloads/point/<period>/<package>

Replace <package> with your package name and <period> with one of these supported values:

  • last-day
  • last-week
  • last-month
  • last-year

Packaging it up into a function for Next.js

Now we can create a simple function that will fetch the data for us and cache it for an hour.

That's where Next.js' revalidate comes into play.

You can lower the value if you want to have more up-to-date stats, but keep in mind that it will cause more incremental static regenerations (ISR) on Vercel when people visit your site.

npm-stats.tsx
1type Period = "last-day" | "last-week" | "last-month" | "last-year";
2
3async function getNPMPackageDownloads(packageName: string, period: Period) {
4 const res = await fetch(
5 `https://api.npmjs.org/downloads/point/${period}/${packageName}`,
6 {
7 next: { revalidate: 3600 }, // 60 (s) * 60 (min) = 3600 seconds (1 hour)
8 },
9 );
10 return res.json();
11}

⚠️ Warning: If you omit the revalidate option, the data will remain cached and your stats will be stale even across deploys.

Displaying the data

Using the function inside a React Server Component is as simple as importing a hook. Here is an example:

page.tsx
1export async function Projects() {
2 const { downloads } = await getNPMPackageDownloads(
3 "react-farcaster-embed",
4 "last-week",
5 );
6
7 return (
8 <p>
9 react-farcaster-embed has ${downloads.toLocaleString()} weekly
10 downloads
11 </p>
12 );
13}

If you want to do the same thing for GitHub stars, you can use the GitHub API and extend your function with another fetch. Just remember to use Promise.all() for best performance.

REST API endpoints for starring - GitHub DocsUse the REST API to bookmark a repository.docs.github.com/en/rest/activity/starring

get notified when i write something new or launch a new project right into your inbox.

or

subscribe on telegram

Thanks for your time & attention.