Blog Home

Core Commerce Funnel: Product, Part 1

We've been looking at Reaction's Core Commerce Funnel as a part of our series on platform architecture. For the next few weeks, I’ll be digging into Products and the Product Detail Page, or PDP, which is a detailed view of a single product.

This week, let's focus on the guts. In this blog post, I'll show you how to extend the core Product schema. We'll get into the PDP next time. Plus, it'll make more sense once you have a basic understanding of how Reaction products work.

Product vs. Product Variants

Products in Reaction have one of two primary schemas: the Product schema or the ProductVariant schema.

Open Robomongo and go to the Products collection on a fresh development install of Reaction with fixture data.


Click on Product and see that while there is only one product loaded, there are actually four products in the Products collection. Reaction Products, Variants, and Options are all stored in the Products collection as individual documents and linked together with the ancestors property:

Basic Reaction Product (_id: P1) // Product schema
|---Basic Example Variant (_id: P2, ancestors: [P1]) // ProductVariant schema
    |---Red (_id: P3, ancestors: [P1, P2]) // ProductVariant schema
    |---Green (_id: P4, ancestors: [P1, P2]) // ProductVariant schema

The top-level product here uses the Product schema. All of the variants and options that this product uses are in the ProductVariant schema. In the core Reaction UI, we refer to the first level of ProductVariants as variants, and the second level as options.


In this example, you can see that this Basic Reaction Product has 1 Example Variant with 2 options: Red and Green. Red and Green share some information, like Manufacturer and Vendor, but have different prices, colors, and photos.

The Product schema is where you save, set, and store information that doesn't change from variant to variant, such as tags, vendor, shopId, and type, which I'll get into in a bit. It's also where you store configuration, such as shipping options, social sharing messages, the product's handle, as well as fields that aggregate from the product's variants, like price or availability.

The ProductVariant schema is where you save, set, and store information that's specific to a variant or option of a product. This information does vary, eg. a skateboard can have various sizes and colors, which come at different prices. Every product must have at least one variant. The base-level variant, a variant without any sub-variants, will determine its own price, weight, dimensions, quantity, etc. If a variant has sub-variants or options, then its properties will be stored, but not used for price, quantity, etc.

I use the term 'base-level variant' because Reaction supports unlimited nesting of ProductVariants everywhere, except for the default UI. If you needed a product that had four levels of variants, then the only thing that you would need to build is the PDP UI for browsing and administering those variants.

When a product is added to cart, we are adding the base-level variant. So, for instance, when a shopper adds that 8.0" blue skateboard deck to their cart, the Reaction app is adding the base-level variant to the user's order. We can access other attributes of by looking those products up as well. We'll discuss the cart in much more depth later on in this series.

Product Types & Multi-Type Schema Validation

Products and ProductVariants have a type (Product, ProductVariant) field. The default values for this are "simple" for Product and "variant" for ProductVariant. If you need different fields in your product schema outside of our default values, then you might consider creating your own product type, eg. "downloads". You could also have a different Product schema that validates for "downloads"-type products only, although our built-in support does not include this.

Look at the code used to attach schemas to collections and all this will make more sense. This code is in /lib/collections/collections.js.

export const Products = new Mongo.Collection("Products");

Products.attachSchema(Schemas.Product, { selector: { type: "simple" } });
Products.attachSchema(Schemas.ProductVariant, { selector: { type: "variant" } });

As you can see here, the Product schema is attached to the Products collection, where the type is equal to "simple".

In this vein, you could theoretically create a Downloads schema and have the "downloads" product type run alongside the "simple" product type, and each would validate correctly. You can do all of this from a plugin, all without having to modify the core code.

Products.attachSchema(Schema.Product, { selector: { type: “downloads” }))

Important: This "selector"-based schema can be confusing if you're not used to it. Because multiple schemas live in a single collection, when inserting or updating Products, you’ll need to be specific about which Product schema you want to check against.

With most collections, you may update with a Collection.update({})

When updating the Products collection, you'll need to specify a schema selector.

It's worth noting that we've added a few other mostly undocumented options, which you can specify alongside the schema selector.

In the Shopify Connector, these options are used to publish products directly after an update. This way, operators don't end up with thousands of unpublished products that need to be published immediately after importing or syncing.

Check out an example of this in the connectors-shopify plugin, shopifyConnect.js.


I hope this deep dive was helpful to your understanding of how Products are treated in Reaction. Now, not only do you know how to create your own product types, but you also now know how to extend the Product schema to meet the specific needs of your store.

If you've built any cool product plugins for Reaction, let us know!

And as always, if you have any feedback or questions, leave a comment below or join our community via Gitter chat or Forums.

comments powered by Disqus