@reinforcezwei/favicon-fetcher
v1.0.5
Published
A Node.js library to fetch website favicons and titles with optional image metadata extraction
Maintainers
Readme
Favicon Fetcher
A Node.js library to fetch website favicons and titles with optional image metadata extraction. The library uses browser-like headers to bypass basic bot detection.
Features
- 🌐 Fetch website favicons and page titles from multiple sources
- 🏷️ Extract titles with rich metadata (HTML, OpenGraph, Twitter, manifest)
- 📝 Extract descriptions with rich metadata (HTML, OpenGraph, Twitter, manifest)
- 🔍 Parse multiple icon sources (HTML, manifest.json, default favicon.ico)
- 📊 Optional image metadata extraction (dimensions, format, file size)
- 🤖 Browser-like headers to bypass basic bot detection
- ⚡ Support for various icon types (favicon, apple-touch-icon, manifest icons)
- 🔗 Automatic URL resolution for relative paths
- 📘 Full TypeScript support with comprehensive type definitions
- 🎯 ES Modules with modern JavaScript syntax
- 🛡️ Graceful error handling with partial results and detailed error reporting
Installation
npm installDependencies
axios- HTTP client for fetching URLscheerio- Fast HTML parserimage-size- Extract image dimensions and format
Usage
TypeScript
This library is written in TypeScript and provides full type definitions out of the box.
import { fetchFavicon, type FetchResult, type Icon } from '@reinforcezwei/favicon-fetcher';
const result: FetchResult = await fetchFavicon('https://github.com');
console.log('Title:', result.title);
console.log('Icons:', result.icons);JavaScript (ES Modules)
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
const result = await fetchFavicon('https://github.com');
console.log('Title:', result.title);
console.log('Icons:', result.icons);Basic Usage
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
try {
const result = await fetchFavicon('https://github.com');
console.log('Title:', result.title);
console.log('URL:', result.url);
console.log('Icons found:', result.icons.length);
result.icons.forEach(icon => {
console.log(`- ${icon.type} (${icon.sizes || 'no size'}): ${icon.url}`);
});
} catch (error) {
console.error('Error:', error.message);
}Extract Titles from Multiple Sources
The library extracts titles from various sources and returns them with rich metadata:
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
try {
const result = await fetchFavicon('https://github.com');
// Use the primary title (for backward compatibility)
console.log('Primary Title:', result.title);
// Access all titles with metadata
console.log('\nAll Titles:');
result.titles.forEach(title => {
console.log(`- ${title.value}`);
console.log(` Source: ${title.source}`);
console.log(` Property: ${title.property}`);
});
// Example output:
// - GitHub · Change is constant
// Source: html
// Property: title
// - GitHub
// Source: opengraph
// Property: og:title
// - GitHub
// Source: twitter
// Property: twitter:title
// - GitHub
// Source: manifest
// Property: name
} catch (error) {
console.error('Error:', error.message);
}Extract Descriptions from Multiple Sources
The library extracts descriptions from various sources with rich metadata:
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
try {
const result = await fetchFavicon('https://github.com');
// Access all descriptions with metadata
console.log('All Descriptions:');
result.descriptions.forEach(description => {
console.log(`- ${description.value}`);
console.log(` Source: ${description.source}`);
console.log(` Property: ${description.property}`);
});
// Example output:
// - GitHub is where people build software...
// Source: html
// Property: description
// - GitHub is where over 100 million developers...
// Source: opengraph
// Property: og:description
// - GitHub is where over 100 million developers...
// Source: twitter
// Property: twitter:description
// - Collaborate on code with GitHub
// Source: manifest
// Property: description
} catch (error) {
console.error('Error:', error.message);
}With Image Metadata
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
try {
const result = await fetchFavicon('https://example.com', {
includeMetadata: true,
timeout: 5000
});
console.log('Title:', result.title);
result.icons.forEach(icon => {
console.log(`\nIcon: ${icon.type}`);
console.log(`URL: ${icon.url}`);
if (icon.metadata) {
console.log(`Dimensions: ${icon.metadata.width}x${icon.metadata.height}`);
console.log(`Format: ${icon.metadata.format}`);
console.log(`Size: ${icon.metadata.size} bytes`);
}
});
} catch (error) {
console.error('Error:', error.message);
}Custom Options
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
const options = {
includeMetadata: false, // Enable/disable image metadata extraction
timeout: 10000, // Request timeout in milliseconds
userAgent: 'CustomBot/1.0' // Custom user agent (optional)
};
const result = await fetchFavicon('https://example.com', options);TypeScript with Type Safety
import { fetchFavicon, type FetchOptions, type FetchResult } from '@reinforcezwei/favicon-fetcher';
const options: FetchOptions = {
includeMetadata: true,
timeout: 10000,
userAgent: 'MyBot/1.0'
};
const result: FetchResult = await fetchFavicon('https://example.com', options);
// TypeScript provides autocomplete and type checking
result.titles.forEach(title => {
console.log(title.value); // string
console.log(title.source); // 'html' | 'opengraph' | 'twitter' | 'manifest'
console.log(title.property); // string
});
result.descriptions.forEach(description => {
console.log(description.value); // string
console.log(description.source); // 'html' | 'opengraph' | 'twitter' | 'manifest'
console.log(description.property); // string
});
result.icons.forEach(icon => {
console.log(icon.url); // string
console.log(icon.type); // string
console.log(icon.source); // 'html' | 'manifest' | 'default'
if (icon.metadata) {
console.log(icon.metadata.width); // number
console.log(icon.metadata.height); // number
console.log(icon.metadata.format); // string
}
});API
fetchFavicon(url, options)
Fetches favicon and title from a given URL.
Parameters
- url (string, required): The URL to fetch favicon from
- options (object, optional): Configuration options
includeMetadata(boolean, default:false): Include image metadatatimeout(number, default:10000): Request timeout in millisecondsuserAgent(string, optional): Custom user agent string
Returns
Promise that resolves to an object with the following structure:
{
url: string, // Original/normalized URL
title: string, // Page title (primary, for backward compatibility)
titles: [ // Array of titles from all sources with metadata
{
value: string, // Title text
source: string, // Source type (html, opengraph, twitter, manifest)
property: string // Property name (title, og:title, twitter:title, name, etc.)
}
],
descriptions: [ // Array of descriptions from all sources with metadata
{
value: string, // Description text
source: string, // Source type (html, opengraph, twitter, manifest)
property: string // Property name (description, og:description, twitter:description)
}
],
icons: [
{
url: string, // Absolute icon URL
type: string, // Icon type (favicon, apple-touch-icon, etc.)
sizes: string, // Size attribute (e.g., "192x192")
source: string, // Where found (html, manifest, default)
metadata: { // Optional (if includeMetadata: true)
width: number, // Image width in pixels
height: number, // Image height in pixels
format: string, // Image format (png, ico, svg, etc.)
size: number // File size in bytes
}
}
],
errors: [ // Optional array of non-critical errors
{
step: string, // Step where error occurred
message: string, // Error message
url: string // Optional URL that caused the error
}
]
}Throws
- Error if URL is invalid or malformed
- Error if HTML fetch fails (network error, timeout, DNS failure, HTTP 4xx/5xx)
- Note: Non-critical errors (title parsing, manifest fetching, metadata extraction) do not throw. Instead, they return partial results with an
errorsarray.
TypeScript Types
The library exports the following TypeScript types for your convenience:
import {
fetchFavicon,
type FetchOptions,
type FetchResult,
type FetchError,
type Icon,
type Title,
type Description,
type ImageMetadata,
type RequestOptions,
type ManifestIcon,
type WebAppManifest
} from '@reinforcezwei/favicon-fetcher';FetchOptions
Configuration options for favicon fetching:
interface FetchOptions {
includeMetadata?: boolean; // Include image metadata
timeout?: number; // Request timeout in ms
userAgent?: string; // Custom user agent
}FetchResult
Result returned by fetchFavicon():
interface FetchResult {
url: string; // Normalized URL
title: string; // Page title (primary, for backward compatibility)
titles: Title[]; // Array of titles from all sources with metadata
descriptions: Description[]; // Array of descriptions from all sources with metadata
icons: Icon[]; // Array of found icons
errors?: FetchError[]; // Optional array of non-critical errors
}FetchError
Error information for non-critical failures:
interface FetchError {
step: string; // Step where error occurred (e.g., 'parse_title', 'fetch_manifest')
message: string; // Error message
url?: string; // Optional URL that caused the error
}Icon
Icon object structure:
interface Icon {
url: string; // Absolute icon URL
type: string; // Icon type
sizes: string; // Size attribute
source: 'html' | 'manifest' | 'default'; // Source
metadata?: { // Optional metadata
width: number;
height: number;
format: string;
size: number;
buffer: Buffer;
};
}Title
Title object structure with source metadata:
interface Title {
value: string; // Title text
source: 'html' | 'opengraph' | 'twitter' | 'manifest'; // Source type
property: string; // Property name (e.g., 'title', 'og:title', 'twitter:title', 'name', 'short_name')
}Description
Description object structure with source metadata:
interface Description {
value: string; // Description text
source: 'html' | 'opengraph' | 'twitter' | 'manifest'; // Source type
property: string; // Property name (e.g., 'description', 'og:description', 'twitter:description')
}ImageMetadata
Image metadata structure (when includeMetadata: true):
interface ImageMetadata {
width: number; // Width in pixels
height: number; // Height in pixels
format: string; // Image format (png, ico, jpeg, etc.)
size: number; // File size in bytes
buffer: Buffer; // Raw image data
}Icon Detection
The library searches for icons in the following order:
HTML
<link>tags:<link rel="icon"><link rel="shortcut icon"><link rel="apple-touch-icon"><link rel="apple-touch-icon-precomposed"><link rel="mask-icon">
Web App Manifest (
manifest.json):- Fetches and parses icons from manifest file
Default Favicon:
- Falls back to
/favicon.icoif no icons found
- Falls back to
OpenGraph Image (fallback):
<meta property="og:image">if no other icons found
Title Detection
The library extracts titles from multiple sources with rich metadata:
HTML Sources
HTML
<title>tag:<title>Page Title</title>- Source:
html - Property:
title
- Source:
OpenGraph title:
<meta property="og:title" content="OpenGraph Title">- Source:
opengraph - Property:
og:title
- Source:
Twitter title:
<meta name="twitter:title" content="Twitter Title">- Source:
twitter - Property:
twitter:title
- Source:
Manifest Sources
- Manifest name:
{ "name": "App Name", "short_name": "App" }- Source:
manifest - Property:
nameorshort_name
- Source:
All titles are returned in the titles array, while the primary HTML title is also available in the title field for backward compatibility.
Description Detection
The library extracts descriptions from multiple sources with rich metadata:
HTML Sources
HTML meta description:
<meta name="description" content="Page description">- Source:
html - Property:
description
- Source:
OpenGraph description:
<meta property="og:description" content="OpenGraph description">- Source:
opengraph - Property:
og:description
- Source:
Twitter description:
<meta name="twitter:description" content="Twitter description">- Source:
twitter - Property:
twitter:description
- Source:
Manifest Sources
- Manifest description:
{ "description": "App description" }- Source:
manifest - Property:
description
- Source:
All descriptions are returned in the descriptions array with their respective source and property metadata.
Browser-Like Headers
The library uses the following headers to mimic browser behavior:
{
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
'Accept': 'text/html,application/xhtml+xml,...',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none'
}Error Handling
The library implements graceful error handling with partial results:
Critical vs Non-Critical Errors
Critical Error (throws exception):
- HTML Fetch Failure: If the initial HTML cannot be fetched, the function throws an error. This is the only critical step because all other operations depend on it.
Non-Critical Errors (returns partial results):
- Title Parsing: Returns empty string if parsing fails
- Multiple Title Parsing: Returns empty array if parsing from multiple sources fails
- Description Parsing: Returns empty array if parsing from multiple sources fails
- Icon Link Parsing: Returns empty array if parsing fails
- Manifest URL Parsing: Skips manifest step if parsing fails
- Manifest Fetching: Continues without manifest icons/titles/descriptions if fetch fails
- Metadata Extraction: Returns icons without metadata if extraction fails
Error Reporting
Non-critical errors are collected and returned in an optional errors array:
const result = await fetchFavicon('https://example.com', { includeMetadata: true });
// Check if any non-critical errors occurred
if (result.errors && result.errors.length > 0) {
console.log('Some operations failed:');
result.errors.forEach(error => {
console.log(`- ${error.step}: ${error.message}`);
if (error.url) {
console.log(` URL: ${error.url}`);
}
});
}
// Still able to access partial results
console.log('Title:', result.title);
console.log('Icons found:', result.icons.length);Error Steps
The following step identifiers may appear in the errors array:
parse_title- Failed to parse page titleparse_titles- Failed to parse titles from multiple sourcesparse_descriptions- Failed to parse descriptions from multiple sourcesparse_icon_links- Failed to parse icon links from HTMLget_manifest_url- Failed to extract manifest URLfetch_manifest- Failed to fetch or parse manifest.jsonadd_metadata- Failed to add metadata to icons
Example: Handling Errors
import { fetchFavicon } from '@reinforcezwei/favicon-fetcher';
try {
const result = await fetchFavicon('https://example.com');
// Check for partial failures
if (result.errors) {
console.warn(`⚠️ ${result.errors.length} non-critical error(s) occurred`);
result.errors.forEach(err => {
console.warn(` - ${err.step}: ${err.message}`);
});
}
// Use partial results
console.log(`✓ Successfully fetched ${result.icons.length} icon(s)`);
console.log(`✓ Title: ${result.title || '(no title)'}`);
} catch (error) {
// Only critical HTML fetch failures throw
console.error('✗ Critical error - could not fetch page:', error.message);
}Resilient Metadata Fetching
When includeMetadata: true is set, the library uses Promise.allSettled to fetch metadata for all icons. If some icons fail, others will still have their metadata extracted:
const result = await fetchFavicon('https://example.com', { includeMetadata: true });
const successCount = result.icons.filter(icon => icon.metadata).length;
const failCount = result.icons.length - successCount;
console.log(`Metadata: ${successCount} successful, ${failCount} failed`);Architecture
The project is structured as follows:
src/
├── index.ts # Main entry point & public API
├── types.ts # TypeScript type definitions
├── fetcher.ts # HTTP client with browser-like headers
├── parser.ts # HTML parsing for icons and title
├── manifest.ts # Manifest.json handler
└── metadata.ts # Image metadata extractorCompiled JavaScript and type definitions are output to the dist/ directory.
Development
Building from Source
- Install dependencies:
npm install- Build the project:
npm run buildThis compiles TypeScript files from src/ to JavaScript in dist/, generating:
.jsfiles (compiled JavaScript).d.tsfiles (TypeScript type definitions).js.mapand.d.ts.mapfiles (source maps)
Scripts
npm run build- Compile TypeScript to JavaScriptnpm run dev- Watch mode for developmentnpm test- Run testsnpm run example- Run example scripts
License
ISC
