google-feeder
v0.1.1
Published
Minimal-dependency TypeScript helpers for generating Google Merchant Center XML feeds.
Readme
google-feeder
Minimal-dependency TypeScript helpers for generating Google Merchant Center XML feeds.
The package currently supports:
- RSS 2.0 feeds with the
g:Merchant Center namespace - Atom 1.0 feeds with the
g:Merchant Center namespace - Standalone products and explicit product variant groups
- Validation modes for warning-only, strict, or disabled validation
- Object-first rendering APIs and a small builder API
Install
npm install google-feederQuick Start
import { renderRss, type MerchantProduct } from "google-feeder";
const product: MerchantProduct = {
id: "sku-123",
title: "Basic Tee",
description: "A plain cotton tee",
link: "https://example.com/products/basic-tee",
image_link: "https://example.com/images/basic-tee.jpg",
availability: "in_stock",
condition: "new",
price: { value: 19.99, currency: "USD" },
brand: "Acme"
};
const result = await renderRss({
channel: {
title: "Example Store",
link: "https://example.com",
description: "Main product feed"
},
catalog: [product],
pretty: true
});
console.log(result.content);
console.log(result.diagnostics);result.content is the XML document. result.diagnostics contains validation warnings or errors collected during rendering.
RSS 2.0
Use renderRss() to get the full feed as a string, or streamRss() to stream chunks for larger catalogs.
import { renderRss } from "google-feeder";
const { content, diagnostics } = await renderRss({
channel: {
title: "Example Store",
link: "https://example.com",
description: "Google Merchant Center feed"
},
catalog: products,
validation: "warn",
pretty: true
});RSS output includes standard <item> fields like title, link, and description, plus matching g:title, g:link, and g:description fields for Merchant Center compatibility.
Atom 1.0
Use renderAtom() for a full string or streamAtom() for chunked output.
import { renderAtom } from "google-feeder";
const { content } = await renderAtom({
feed: {
title: "Example Store Feed",
id: "tag:example.com,2026:/feeds/catalog",
link: "https://example.com",
updated: "2026-01-01T00:00:00Z",
authorName: "Example Store"
},
catalog: products,
pretty: true
});Atom feed metadata is format-specific and requires:
titleidlinkupdatedauthorName
subtitle is optional.
Variants
Google product variants are modeled explicitly with ProductVariantGroup. Each variant is rendered as an individual item or entry, and the library injects item_group_id automatically.
import { renderAtom, type ProductVariantGroup } from "google-feeder";
const group: ProductVariantGroup = {
itemGroupId: "shirt-1",
variesBy: ["color", "size"],
shared: {
title: "Variant Tee",
description: "Variant tee for grouped rendering",
link: "https://example.com/products/variant-tee",
image_link: "https://example.com/images/variant-default.jpg",
availability: "in_stock",
condition: "new",
price: { value: 29.99, currency: "USD" },
brand: "Acme"
},
variants: [
{
id: "shirt-1-red-s",
color: "Red",
size: "S",
link: "https://example.com/products/variant-tee-red-s",
image_link: "https://example.com/images/variant-tee-red-s.jpg"
},
{
id: "shirt-1-blue-m",
color: "Blue",
size: "M",
link: "https://example.com/products/variant-tee-blue-m",
image_link: "https://example.com/images/variant-tee-blue-m.jpg"
}
]
};
const result = await renderAtom({
feed: {
title: "Variants",
id: "tag:example.com,2026:/feeds/variants",
link: "https://example.com",
updated: "2026-01-01T00:00:00Z",
authorName: "Example Store"
},
catalog: [group]
});Variant validation covers:
- at least one supported variant axis
- required values for every axis on every variant
- unique variant-value combinations within a group
- duplicate product ids
- duplicate explicit
item_group_idgroups
Supported variant axes are:
colorsizematerialpatternage_groupgender
Builder API
If you want to incrementally assemble a catalog, use createMerchantFeed().
import { createMerchantFeed } from "google-feeder";
const feed = createMerchantFeed({
validation: "warn",
pretty: true
});
feed.add(product);
feed.add(group);
feed.addMany(otherProducts);
const rss = await feed.toRss({
title: "Example Store",
link: "https://example.com",
description: "Main RSS feed"
});
const atom = await feed.toAtom({
title: "Example Store",
id: "tag:example.com,2026:/feeds/catalog",
link: "https://example.com",
updated: "2026-01-01T00:00:00Z",
authorName: "Example Store"
});Streaming is also available:
for await (const chunk of feed.toRssChunks({
title: "Example Store",
link: "https://example.com",
description: "Main RSS feed"
})) {
process.stdout.write(chunk);
}Validation
Validation can run in three modes:
"warn": collect diagnostics and keep rendering"strict": throwValidationErroron the first error"off": skip diagnostics
Examples:
import { ValidationError, renderRss, validateCatalog, validateProduct } from "google-feeder";
const diagnostics = validateProduct(product, { validation: "warn" });
const catalogDiagnostics = await validateCatalog({
catalog: [product, group],
validation: "warn"
});
try {
await renderRss({
channel: {
title: "Strict feed",
link: "https://example.com",
description: "Strict feed"
},
catalog: [product],
validation: "strict"
});
} catch (error) {
if (error instanceof ValidationError) {
console.error(error.diagnostics);
}
}Diagnostics include:
severitycodemessageattributeproductIditemGroupIdspecUrl
Supported Attribute Shapes
The package supports a broad typed surface for the standard Merchant Center product feed, including:
- simple scalar attributes such as
id,title,brand,condition,item_group_id - repeated attributes such as
additional_image_link,product_highlight,included_destination - grouped attributes such as
installment,subscription_cost,loyalty_program - repeated grouped attributes such as
shipping,product_detail,certification,tax
Helpful input formats:
- money values can be
"19.99 USD"or{ value: 19.99, currency: "USD" } - measures can be
"2.5 kg"or{ value: 2.5, unit: "kg" } - booleans can be
true/falseor"yes"/"no"
Notes
- Runtime dependencies: none
- Target runtime: Node.js
>=18 - Package output: ESM, CommonJS, and generated
.d.tsdeclarations - Scope is the standard Merchant Center product feed only; it does not cover local inventory, promotions, reviews, or vehicle feeds
Development
npm install
npm test