@saketsawrav/instagram-feed 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,7 @@ Drop-in Instagram feed for Next.js apps. Fetches recent posts and proxies images
5
5
  ## Features
6
6
 
7
7
  - Fetches posts via Instagram's public web profile API
8
+ - Post captions included out of the box
8
9
  - Server-side image proxy (avoids Instagram's cross-origin blocking)
9
10
  - In-memory caching with configurable TTL (default: 1 hour)
10
11
  - Ready-made Next.js route handler factories
@@ -97,7 +98,7 @@ import { fetchInstagramPosts } from '@saketsawrav/instagram-feed';
97
98
 
98
99
  const posts = await fetchInstagramPosts({ username: 'your_username', count: 6 });
99
100
  console.log(posts);
100
- // [{ shortcode: 'abc123', thumbnailUrl: 'https://...' }, ...]
101
+ // [{ shortcode: 'abc123', thumbnailUrl: 'https://...', caption: 'Post caption text...' }, ...]
101
102
  ```
102
103
 
103
104
  ### Available exports from `@saketsawrav/instagram-feed`
@@ -107,7 +108,7 @@ console.log(posts);
107
108
  | `fetchInstagramPosts()` | Fetch posts for a username (returns `InstagramPost[]`) |
108
109
  | `getInstagramImageUrl()`| Get the full-res image URL for a shortcode |
109
110
  | `proxyInstagramImage()` | Fetch and stream an image from Instagram CDN |
110
- | `InstagramPost` | Type: `{ shortcode: string; thumbnailUrl: string }` |
111
+ | `InstagramPost` | Type: `{ shortcode: string; thumbnailUrl: string; caption: string \| null }` |
111
112
  | `InstagramFeedOptions` | Type: configuration options |
112
113
 
113
114
  ### Available exports from `@saketsawrav/instagram-feed/nextjs`
@@ -0,0 +1,93 @@
1
+ // src/fetcher.ts
2
+ var DEFAULT_COUNT = 9;
3
+ var DEFAULT_CACHE_TTL_MS = 36e5;
4
+ var DEFAULT_IG_APP_ID = "936619743392459";
5
+ var USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36";
6
+ var cacheByUsername = /* @__PURE__ */ new Map();
7
+ function buildHeaders(username, igAppId) {
8
+ return {
9
+ "User-Agent": USER_AGENT,
10
+ "x-ig-app-id": igAppId,
11
+ Accept: "*/*",
12
+ "Sec-Fetch-Mode": "cors",
13
+ "Sec-Fetch-Site": "same-origin",
14
+ "Sec-Fetch-Dest": "empty",
15
+ Referer: `https://www.instagram.com/${username}/`
16
+ };
17
+ }
18
+ async function fetchFromInstagram(username, count, igAppId) {
19
+ const res = await fetch(
20
+ `https://www.instagram.com/api/v1/users/web_profile_info/?username=${username}`,
21
+ { headers: buildHeaders(username, igAppId) }
22
+ );
23
+ if (!res.ok) {
24
+ return { posts: [], imageMap: {}, timestamp: Date.now() };
25
+ }
26
+ const json = await res.json();
27
+ const edges = json?.data?.user?.edge_owner_to_timeline_media?.edges ?? [];
28
+ const posts = [];
29
+ const imageMap = {};
30
+ for (const edge of edges) {
31
+ const node = edge?.node;
32
+ if (!node?.shortcode) continue;
33
+ const thumbnailUrl = node.thumbnail_src ?? node.display_url ?? "";
34
+ const displayUrl = node.display_url ?? node.thumbnail_src ?? "";
35
+ imageMap[node.shortcode] = displayUrl;
36
+ const caption = node.edge_media_to_caption?.edges?.[0]?.node?.text ?? null;
37
+ if (posts.length < count) {
38
+ posts.push({ shortcode: node.shortcode, thumbnailUrl, caption });
39
+ }
40
+ }
41
+ return { posts, imageMap, timestamp: Date.now() };
42
+ }
43
+ function getCached(username, cacheTtlMs) {
44
+ const entry = cacheByUsername.get(username);
45
+ if (entry && Date.now() - entry.timestamp < cacheTtlMs) return entry;
46
+ return null;
47
+ }
48
+ async function ensureCache(opts) {
49
+ const existing = getCached(opts.username, opts.cacheTtlMs);
50
+ if (existing) return existing;
51
+ const fresh = await fetchFromInstagram(opts.username, opts.count, opts.igAppId);
52
+ if (fresh.posts.length > 0) {
53
+ cacheByUsername.set(opts.username, fresh);
54
+ }
55
+ return fresh;
56
+ }
57
+ function resolveOptions(opts) {
58
+ return {
59
+ username: opts.username,
60
+ count: opts.count ?? DEFAULT_COUNT,
61
+ cacheTtlMs: opts.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS,
62
+ igAppId: opts.igAppId ?? DEFAULT_IG_APP_ID
63
+ };
64
+ }
65
+ async function fetchInstagramPosts(opts) {
66
+ const resolved = resolveOptions(opts);
67
+ const cached = await ensureCache(resolved);
68
+ return cached.posts;
69
+ }
70
+ async function getInstagramImageUrl(opts, shortcode) {
71
+ const resolved = resolveOptions(opts);
72
+ const cached = await ensureCache(resolved);
73
+ return cached.imageMap[shortcode] ?? null;
74
+ }
75
+ async function proxyInstagramImage(imageUrl) {
76
+ const res = await fetch(imageUrl, {
77
+ headers: {
78
+ "User-Agent": USER_AGENT,
79
+ Referer: "https://www.instagram.com/"
80
+ }
81
+ });
82
+ if (!res.ok || !res.body) return null;
83
+ return {
84
+ body: res.body,
85
+ contentType: res.headers.get("content-type") ?? "image/jpeg"
86
+ };
87
+ }
88
+
89
+ export {
90
+ fetchInstagramPosts,
91
+ getInstagramImageUrl,
92
+ proxyInstagramImage
93
+ };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as InstagramFeedOptions, a as InstagramPost } from './types-CJuNiTch.mjs';
1
+ import { I as InstagramFeedOptions, a as InstagramPost } from './types-Baezfam3.mjs';
2
2
 
3
3
  declare function fetchInstagramPosts(opts: InstagramFeedOptions): Promise<InstagramPost[]>;
4
4
  declare function getInstagramImageUrl(opts: InstagramFeedOptions, shortcode: string): Promise<string | null>;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as InstagramFeedOptions, a as InstagramPost } from './types-CJuNiTch.js';
1
+ import { I as InstagramFeedOptions, a as InstagramPost } from './types-Baezfam3.js';
2
2
 
3
3
  declare function fetchInstagramPosts(opts: InstagramFeedOptions): Promise<InstagramPost[]>;
4
4
  declare function getInstagramImageUrl(opts: InstagramFeedOptions, shortcode: string): Promise<string | null>;
package/dist/index.js CHANGED
@@ -61,8 +61,9 @@ async function fetchFromInstagram(username, count, igAppId) {
61
61
  const thumbnailUrl = node.thumbnail_src ?? node.display_url ?? "";
62
62
  const displayUrl = node.display_url ?? node.thumbnail_src ?? "";
63
63
  imageMap[node.shortcode] = displayUrl;
64
+ const caption = node.edge_media_to_caption?.edges?.[0]?.node?.text ?? null;
64
65
  if (posts.length < count) {
65
- posts.push({ shortcode: node.shortcode, thumbnailUrl });
66
+ posts.push({ shortcode: node.shortcode, thumbnailUrl, caption });
66
67
  }
67
68
  }
68
69
  return { posts, imageMap, timestamp: Date.now() };
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  fetchInstagramPosts,
3
3
  getInstagramImageUrl,
4
4
  proxyInstagramImage
5
- } from "./chunk-QEYBYUKH.mjs";
5
+ } from "./chunk-KW22UH7A.mjs";
6
6
  export {
7
7
  fetchInstagramPosts,
8
8
  getInstagramImageUrl,
package/dist/nextjs.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as InstagramFeedOptions, a as InstagramPost } from './types-CJuNiTch.mjs';
1
+ import { I as InstagramFeedOptions, a as InstagramPost } from './types-Baezfam3.mjs';
2
2
  import { NextResponse, NextRequest } from 'next/server';
3
3
 
4
4
  declare function createFeedHandler(opts: InstagramFeedOptions): () => Promise<NextResponse<InstagramPost[]>>;
package/dist/nextjs.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as InstagramFeedOptions, a as InstagramPost } from './types-CJuNiTch.js';
1
+ import { I as InstagramFeedOptions, a as InstagramPost } from './types-Baezfam3.js';
2
2
  import { NextResponse, NextRequest } from 'next/server';
3
3
 
4
4
  declare function createFeedHandler(opts: InstagramFeedOptions): () => Promise<NextResponse<InstagramPost[]>>;
package/dist/nextjs.js CHANGED
@@ -63,8 +63,9 @@ async function fetchFromInstagram(username, count, igAppId) {
63
63
  const thumbnailUrl = node.thumbnail_src ?? node.display_url ?? "";
64
64
  const displayUrl = node.display_url ?? node.thumbnail_src ?? "";
65
65
  imageMap[node.shortcode] = displayUrl;
66
+ const caption = node.edge_media_to_caption?.edges?.[0]?.node?.text ?? null;
66
67
  if (posts.length < count) {
67
- posts.push({ shortcode: node.shortcode, thumbnailUrl });
68
+ posts.push({ shortcode: node.shortcode, thumbnailUrl, caption });
68
69
  }
69
70
  }
70
71
  return { posts, imageMap, timestamp: Date.now() };
package/dist/nextjs.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  fetchInstagramPosts,
3
3
  getInstagramImageUrl,
4
4
  proxyInstagramImage
5
- } from "./chunk-QEYBYUKH.mjs";
5
+ } from "./chunk-KW22UH7A.mjs";
6
6
 
7
7
  // src/handlers.ts
8
8
  import { NextResponse } from "next/server";
@@ -0,0 +1,13 @@
1
+ interface InstagramPost {
2
+ shortcode: string;
3
+ thumbnailUrl: string;
4
+ caption: string | null;
5
+ }
6
+ interface InstagramFeedOptions {
7
+ username: string;
8
+ count?: number;
9
+ cacheTtlMs?: number;
10
+ igAppId?: string;
11
+ }
12
+
13
+ export type { InstagramFeedOptions as I, InstagramPost as a };
@@ -0,0 +1,13 @@
1
+ interface InstagramPost {
2
+ shortcode: string;
3
+ thumbnailUrl: string;
4
+ caption: string | null;
5
+ }
6
+ interface InstagramFeedOptions {
7
+ username: string;
8
+ count?: number;
9
+ cacheTtlMs?: number;
10
+ igAppId?: string;
11
+ }
12
+
13
+ export type { InstagramFeedOptions as I, InstagramPost as a };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saketsawrav/instagram-feed",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Drop-in Instagram feed for Next.js — fetches posts and proxies images server-side, no third-party widgets",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",