@weareseeed/medusa-athos-plugin
v0.0.5
Published
Medusa Athos Integration Plugin.
Downloads
600
Readme
@weareseeed/medusa-athos-plugin
Medusa v2 plugin that generates a streaming NDJSON product feed for Athos Commerce integration.
Features
- Streaming NDJSON product feed (one line per variant + one product summary line)
- Token-based feed authentication
- Price filtering by region
- Product filtering by sales channel
- Configurable swatch options
- Extensible via
extraProductFieldsandtransformLinehooks - Admin API to manage plugin configuration
Requirements
- Medusa v2
- Node.js >= 20
Installation
yarn add @weareseeed/medusa-athos-pluginSetup
1. Register the plugin
// medusa-config.ts
import { defineConfig } from "@medusajs/framework/config"
export default defineConfig({
plugins: [
{
resolve: "@weareseeed/medusa-athos-plugin",
options: {},
},
],
})2. Run migrations
npx medusa db:migrate3. Configure via Admin API
Set the plugin configuration through the authenticated admin endpoint:
curl -X POST http://localhost:9000/admin/athos/config \
-H "Authorization: Bearer <admin_token>" \
-H "Content-Type: application/json" \
-d '{
"storefront_url": "https://mystore.com",
"feed_token": "a-secret-token",
"region_id": "reg_01...",
"sales_channel_ids": ["sc_01..."],
"swatch_option_titles": ["color", "colour"]
}'| Field | Type | Required | Description |
|---|---|---|---|
| storefront_url | string | Yes | Base URL prepended to /products/<handle> for each product URL |
| feed_token | string | Yes | Secret token required to access the feed endpoint |
| region_id | string | No | When set, prices are filtered to the region's currency |
| sales_channel_ids | string[] | No | When set, only products in these sales channels are included |
| swatch_option_titles | string[] | No | Option titles treated as swatches (default: ["color", "colour"]) |
Feed Endpoint
GET /athos/feed?token=<feed_token>Returns an application/x-ndjson stream. Each product produces:
- One variant line per variant
- One product summary line
Variant line fields
| Field | Description |
|---|---|
| Product ID | Variant ID |
| SKU | Variant SKU |
| Name | Variant title |
| Product URL | <storefront_url>/products/<handle> |
| Price | Lowest price for the variant (filtered by region if configured) |
| Retail Price | compare_at_price |
| Thumbnail URL | Product thumbnail |
| Description | Product description |
| Category | Array of category path strings (e.g. "Parent>Child") |
| Category ID | Array of category IDs |
| Search Keywords | Comma-separated product tags |
| __parent_id | Parent product ID |
| __parent_title | Parent product title |
| __parent_image | Parent product thumbnail |
| __variant_position | 1-based position within the product |
| __in_stock | true if the variant is available |
| __in_stock_pct | % of variants in stock across the product |
| __standard_options | All product options with positions and values |
| __selected_options | This variant's chosen option values |
| __swatch_options | Values of configured swatch options (product-level) |
Product summary line fields
Same as variant line minus all __ private fields, with Product ID set to the product ID and Price set to the lowest price across all variants.
Example
{"Product ID":"variant_01...","SKU":"1","Name":"blue, m","Product URL":"https://mystore.com/products/example","Price":10,"Thumbnail URL":"https://...","Description":"...","Category":["Tops"],"Category ID":["pcat_01..."],"__parent_id":"prod_01...","__parent_title":"Example Product","__parent_image":"https://...","__variant_position":1,"__in_stock":true,"__in_stock_pct":100,"__standard_options":{"color":{"position":0,"values":["red","blue"]},"size":{"position":1,"values":["s","m"]}},"__selected_options":{"color":{"value":"blue"},"size":{"value":"m"}},"__swatch_options":{"red":{"value":"red"},"blue":{"value":"blue"}}}
{"Product ID":"prod_01...","Name":"Example Product","Product URL":"https://mystore.com/products/example","Price":10,"Thumbnail URL":"https://...","Description":"...","Category":["Tops"],"Category ID":["pcat_01..."]}Plugin Options
Pass options when registering the plugin in medusa-config.ts to extend the feed.
import { defineConfig } from "@medusajs/framework/config"
import type { FeedLineContext } from "@weareseeed/medusa-athos-plugin/types"
export default defineConfig({
plugins: [
{
resolve: "@weareseeed/medusa-athos-plugin",
options: {
feed: {
extraProductFields: ["metadata", "variants.metadata"],
transformLine: (line, ctx: FeedLineContext) => {
line["Brand"] = (ctx.product as any).metadata?.brand ?? undefined
if (ctx.type === "variant") {
line["Custom Variant Field"] = (ctx.variant as any).metadata?.custom ?? undefined
}
if (ctx.type === "product") {
line["Custom Product Field"] = (ctx.product as any).metadata?.custom ?? undefined
}
return line
},
},
},
},
],
})feed.extraProductFields
string[] — Additional Medusa product fields to fetch and make available inside transformLine. Uses the same dot-notation field paths as Medusa's query.graph.
extraProductFields: [
"metadata",
"variants.metadata",
"images.url",
"collection.title",
]feed.transformLine
(line: Record<string, unknown>, ctx: FeedLineContext) =>
Record<string, unknown> | Promise<Record<string, unknown>>Called for every line written to the feed. Use it to add, remove, or transform fields. The ctx argument provides typed access to the raw product and variant data:
type FeedLineContext =
| { type: "variant"; product: Record<string, unknown>; variant: Record<string, unknown>; variantIndex: number }
| { type: "product"; product: Record<string, unknown> }Fields that resolve to
undefinedare automatically stripped from the output.
Admin API
Both endpoints require an authenticated admin JWT.
| Method | Path | Description |
|---|---|---|
| GET | /admin/athos/config | Retrieve the current configuration |
| POST | /admin/athos/config | Create or update the configuration |
License
MIT — Seeed
