Loading…

Star4.1kBook a meeting

Ecommerce

Storefront-facing customer data (wishlists, product reviews, last-viewed items, and addresses) inside the Sales plugin.

The ecommerce module lives inside sales_api (port 3305). It exposes authenticated GraphQL operations for staff, cp* operations marked forClientPortal for storefront customers, and unauthenticated HTTP summary routes for storefront bootstrapping.

Module layout

Backend code lives under backend/plugins/sales_api/src/modules/ecommerce/:

Area Path Responsibility
Models db/definitions, db/models Wishlist, ProductReview, LastViewedItem, Address
GraphQL graphql/schema, graphql/resolvers Admin and cp* (Client Portal) operations
HTTP routes routes.ts (mounted from src/routes.ts) /ecommerce-* summary and bulk endpoints
tRPC trpc/ecommerce.ts Router file exists but is not merged into the plugin appRouter
Utils utils.ts Core products.find, customers.find, productCategories.find fetchers

Data model

  • Wishlist: { productId, customerId } pairs.
  • ProductReview: { productId, customerId, review: Float, description, info: JSON }.
  • LastViewedItem: { productId, customerId, modifiedAt } with a { customerId, productId } index.
  • Address: { alias, customerId, coordinate: {lat,lng}, address1, address2, city, district, street, detail, more: JSON, w3w, note, phone } plus timestamps.

productId/customerId reference Core records; the module never stores product or customer data. It hydrates them over tRPC (core.products.find, core.customers.find, core.productCategories.find).

HTTP routes

Mounted on the plugin Express router (modules/ecommerce/routes.ts), reached through the gateway as /pl:sales/<path>. All are GET unless noted; all take customerId/productId as query params and return { status, data } or { status: 'error', errorMessage }:

Route Params Returns
GET /ecommerce-init customerId Customer summary: reviews + stats, wishlist, last 20 viewed items, addresses, hydrated products
GET /ecommerce-product-summary productId Reviews, rating stats, wishlist/last-viewed counts, reviewer customers
GET /ecommerce-last-viewed customerId, limit (default 10) Last-viewed items with hydrated products
GET /ecommerce-wishlist customerId Wishlist with hydrated products
GET /ecommerce-addresses customerId Address list
GET /ecommerce-product-reviews productId, limit (20), skip, sort (-createdAt) Reviews with hydrated customers plus summary
POST /ecommerce-bulk-operations operations[] Batch write: addToWishlist, addReview, addLastViewed, addAddress

These routes are unauthenticated

/ecommerce-* routes check no token; any caller who knows a customerId can read that customer's wishlist, addresses, and review history. Front them with your own gateway auth if the storefront is public-facing.

Queries and mutations

Wishlist

Operation Kind Notes
wish(productId, customerId) query Single wish, hydrated product
wishlist(customerId) query List, hydrated products
wishlistAdd / wishlistUpdate / wishlistRemove mutation Staff-side mutations
cpWishlist, cpWishlistAdd/Update/Remove query/mutation forClientPortal variants

Product reviews

Operation Kind Notes
productReview(productId) query AverageReview: average + count
productReviews(productIds, customerId, page, perPage) query Paginated list
productReviewAdd/Update/Remove mutation All review mutations are skipPermission
cpProductReviews, cpProductReview query forClientPortal
cpProductReviewAdd/Update/Remove mutation skipPermission

Last viewed items

Operation Kind Notes
lastViewedItems(customerId, limit) query Staff-side
lastViewedItemAdd / lastViewedItemRemove mutation Staff-side
cpLastViewedItems, cpLastViewedItemAdd/Remove query/mutation forClientPortal

Addresses

Operation Kind Notes
address(_id) / addressList(page, perPage, searchValue, aliasType, customerId, city, district, street) query addressList is forClientPortal
addressAdd / addressUpdate / addressRemove mutation addressAdd/addressUpdate are forClientPortal

Permissions and context

The module registers no permission actions of its own. Standard operations run under the platform's default resolver wrapper, which only requires a logged-in user (checkLogin). There are no per-action ecommerce permissions.

Operations marked forClientPortal: true run under wrapPublicResolver instead (wrapperResolvers.ts): they require a Client Portal request context (context.clientPortal) and skip staff login. No ecommerce operation sets cpUserRequired, so a CP user session is not enforced; pass customerId explicitly. See Client Portal app for how that context is established.

tRPC

trpc/ecommerce.ts defines productReview, wishlist, lastViewedItem, and address sub-routers with findOne/find/aggregate/count/create/update/remove helpers, but the file is not imported by src/trpc/init-trpc.ts, so none of these procedures are reachable through the plugin's /trpc mount. Treat the router as dead code; use the HTTP routes or GraphQL instead.

Troubleshooting

  • Customer ID is required / Product ID is required: the routes read query params, not a body; use ?customerId=.
  • Hydrated product is null: the referenced Core product was deleted or belongs to another tenant; list resolvers drop wishes whose product no longer exists.
  • tRPC call to productReview.find fails: the ecommerce router isn't mounted; nothing to fix in config, use HTTP/GraphQL.

Operate on one system.

Talk to our team about consolidating your stack onto erxes. 30-minute walkthrough, custom to your org.