facebook-comments-client
v0.1.5
Published
Fetch comments from Facebook posts (including Group posts) using an authorized Playwright session. Returns clean structured JSON. No database, no queues, no scheduling.
Maintainers
Readme
facebook-comments-client
Fetch comments from Facebook posts — including posts inside Facebook Groups where your account already has legitimate access — using an authorized Playwright session. Returns clean, normalized JSON that is independent of Facebook's internal response structure.
This is a focused library. It contains no database, no Supabase, no queues, no cron jobs, and no backend server. Its sole responsibility:
Facebook Post URL → Authenticate/Reuse Authorized Session → Fetch Available Comments → Return Clean Structured JSONInstallation
npm install facebook-comments-client
# The library drives a real browser via Playwright:
npx playwright install chromiumRequirements: Node.js 20+.
Quick start
import { FacebookCommentsClient } from "facebook-comments-client";
const client = new FacebookCommentsClient({
session: {
type: "file",
path: "./facebook-session.json"
}
});
const result = await client.getComments({
url: "https://www.facebook.com/groups/GROUP_ID/posts/POST_ID/",
maxComments: 100,
includeReplies: true
});
console.log(result.comments);
await client.close();Interactive login
The library never enters credentials for you. It opens a visible browser, you log in manually, and the authorized session is exported:
import { FacebookCommentsClient } from "facebook-comments-client";
await FacebookCommentsClient.login({
outputPath: "./facebook-session.json"
});Or from the CLI:
npx fb-comments login --output ./facebook-session.jsonThe helper waits for the Facebook c_user cookie to appear (up to 5 minutes by default), saves the Playwright storage state, and closes the browser.
Session file usage
const client = new FacebookCommentsClient({
session: { type: "file", path: "./facebook-session.json" }
});type: "storage" is accepted as an alias. The file is a standard Playwright storage state (as produced by context.storageState() and by the login helper above).
Environment variable usage
Handy for deployments. Encode a storage state once:
const encoded = Buffer.from(JSON.stringify(storageState)).toString("base64");Then configure the client:
const client = new FacebookCommentsClient({
session: { type: "env", envKey: "FACEBOOK_STORAGE_STATE" }
});The variable is base64(-url) decoded, JSON parsed, and validated with Zod before a browser context is created. You can also pass a plain cookie array via { type: "cookies", cookies } or an inline object via { type: "state", state }.
Fetching comments
const result = await client.getComments({
url: "https://www.facebook.com/groups/123/posts/456/",
maxComments: 100, // default 50
includeReplies: true, // default false
maxReplies: 20, // default 20, per comment
timeout: 30000, // navigation timeout ms
headless: true // override client default
});Returns a normalized shape:
{
post: { id: "456", url: "https://www.facebook.com/groups/123/posts/456/" },
comments: [
{
id: "comment_123" | null,
author: { id: "user_123" | null, name: "John Doe" | null, profileUrl: "..." | null },
message: "Hello!",
createdAt: "2026-08-28T10:30:00.000Z" | null,
reactions: { total: 5 | null },
parentId: null,
replies: [ /* same shape */ ]
}
],
pagination: { hasMore: false, nextCursor: null }
}Every field degrades to null instead of crashing when Facebook's markup differs — missing profile URLs, unparseable timestamps, unavailable reaction counts, etc.
Fetching replies
Set includeReplies: true (and optionally maxReplies). The fetcher expands "View more replies" controls (bounded) and nests replies under their parent comment with parentId set.
Pagination
const page1 = await client.getComments({ url, maxComments: 25 });
if (page1.pagination.hasMore) {
const page2 = await client.getComments({
url,
maxComments: 25,
cursor: page1.pagination.nextCursor
});
}The cursor is an opaque base64url token — an offset into the comment stream. No Facebook internals leak into your code.
Error handling
import {
FacebookClientError,
FacebookAuthenticationError,
FacebookSessionExpiredError,
FacebookPostNotFoundError,
FacebookAccessDeniedError,
FacebookExtractionError
} from "facebook-comments-client";
try {
const { comments } = await client.getComments({ url: postUrl });
} catch (error) {
if (error instanceof FacebookSessionExpiredError) {
console.log("Facebook session needs to be refreshed.");
}
}Every error carries a stable code (FB_AUTH, FB_SESSION_EXPIRED, FB_POST_NOT_FOUND, FB_ACCESS_DENIED, FB_EXTRACTION, FB_CLIENT), a safe human-readable message, and a retryable boolean. Error messages never contain cookies or storage-state content. See docs/errors.md.
Security notes
- Treat session files as passwords. A storage state grants full access to the logged-in account.
- The library never logs cookie values, tokens, or storage states; its logger redacts sensitive keys, and error messages are scrubbed.
facebook-session.json,*.storage-state.json, and.envare in.gitignore— never commit them.- Environment variables are convenient for deployment, but session expiration and secret handling are your responsibility. Prefer your platform's secret manager over plaintext
.envfiles. - Use sessions belonging to accounts that legitimately have access to the requested content. This library does not bypass logins, CAPTCHAs, or access controls, and contains no stealth, fingerprint-spoofing, proxy-rotation, or restriction-evasion features.
Session refresh instructions
Facebook sessions expire (typically days to weeks):
client.validateSession()returns{ valid: true, authenticated: false }, orgetCommentsthrowsFacebookSessionExpiredError.- Re-run the login helper:
await FacebookCommentsClient.login({ outputPath })(orfb-comments login). - If you use the env-var provider, re-encode the fresh state and update the variable.
Known limitations
- Facebook markup is unstable by nature. All selectors and text patterns live in
src/config/selectors.ts; when extraction quality degrades, that one file is the place to look. Extraction issues throwFacebookExtractionErrorrather than returning garbage silently. - The structured-data extractor is opportunistic; on most modern pages the accessibility-based DOM extractor does the real work.
- Timestamps shown as relative times ("2h") are approximations computed from fetch time.
- Comment ids are frequently absent in the DOM and are returned as
null. - Localization: text patterns currently target English UI text. Non-English Facebook UIs may need additional patterns in
selectors.ts. - Very long threads (hundreds of comments) require pagination; the load-more loop is intentionally bounded.
- Integration tests against live Facebook are opt-in (
tests/integration/live.test.ts) and needFB_TEST_URL+FACEBOOK_STORAGE_STATE.
CLI
# Login (manual, headed browser)
fb-comments login --output ./facebook-session.json
# Fetch comments
fb-comments comments "https://www.facebook.com/groups/123/posts/456/" --replies --limit 100
# Machine-readable output
fb-comments comments POST_URL --json
# Session from env var
FACEBOOK_STORAGE_STATE="..." fb-comments comments POST_URLDevelopment
npm install
npx playwright install chromium
npm test # unit + fixture tests (offline, no Facebook login needed)
npm run build # dist/ ESM + CJS + types + CLI
npm run lintSee docs/api.md for the full API surface, docs/authentication.md and docs/session-management.md for session details.
License
MIT
