dist/codex/salesforce-commerce/skills/sf-b2c-controllers/SKILL.md
Build SFRA controllers — server.get/post/use route handlers, middleware chain with server.append/prepend/replace, CSRF protection, form validation, response rendering, and route-level caching. Use when implementing request handling in B2C Commerce.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins sf-b2c-controllersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build SFRA controllers for request handling in Salesforce B2C Commerce Cloud.
Fetch live documentation FIRST:
Web-search for:
Web-fetch official sources:
github.com/SalesforceCommerceCloud/storefront-reference-architecture (SFRA controllers)Verify before coding: current server module syntax, middleware chain methods, CSRF validation requirements, form validation patterns in latest SFRA.
Controllers are route handlers defined using the server module. Routes follow the naming convention ControllerName-ActionName (e.g., Product-Show, Cart-AddProduct).
controllers/Product.jsProduct-Showhttps://site.com/Product-Show?pid=12345Every controller file ends with module.exports = server.exports();
| Method | HTTP | Purpose | Example |
|--------|------|---------|---------|
| server.get(name, ...mw, handler) | GET | Display pages | Product-Show, Cart-Show |
| server.post(name, ...mw, handler) | POST | Form submissions | Cart-AddProduct, Account-Register |
| server.use(name, ...mw, handler) | Any | Shared logic, API endpoints | Method-agnostic handlers |
Middleware functions execute left to right before the final handler. Each must call next() to continue the chain.
server.post('Action',
server.middleware.https, -> 1. Force HTTPS
csrfProtection.validateRequest, -> 2. Validate CSRF
userLoggedIn.validateLoggedIn, -> 3. Require login
function (req, res, next) {} -> 4. Handler
);
Common built-in middleware:
server.middleware.https -- force HTTPScsrfProtection.validateRequest -- CSRF token validationcsrfProtection.generateToken -- generate CSRF token for formsuserLoggedIn.validateLoggedIn -- require authenticated userconsentTracking.consent -- consent tracking check| Method | What Happens | When to Use |
|--------|-------------|-------------|
| server.extend(base) | Inherit all base routes | Always start with this |
| server.append('Route', fn) | Run AFTER base handler | Add data to response, logging, analytics |
| server.prepend('Route', fn) | Run BEFORE base handler | Validation, guards, tracking |
| server.replace('Route', fn) | Completely override base | Fundamentally different logic (rare) |
| server.get/post('Route', fn) | Define new route | New functionality not in base |
// Pattern: Extend a base controller
var base = module.superModule;
server.extend(base);
// Fetch live docs for append/prepend behavior
HTTP Request
-> Route resolution (cartridge path, left-to-right)
-> server.use middleware (guards, validation)
-> server.prepend extensions
-> Base route handler (if extended)
-> server.append extensions
-> Response (render / json / redirect)
| Method | Use Case |
|--------|----------|
| res.render(template, data) | Render ISML template (HTML page) |
| res.json(object) | Return JSON (AJAX responses) |
| res.redirect(url) | HTTP redirect |
| res.setStatusCode(code) | Set HTTP status (404, 500, etc.) |
| res.setViewData(data) | Set data for middleware chain sharing |
| res.getViewData() | Get data set by previous middleware |
Cache GET responses via property assignment on res:
| Property | Type | Values |
|----------|------|--------|
| res.cachePeriod | Number | Duration value (e.g., 24) |
| res.cachePeriodUnit | String | 'minutes', 'hours', 'days' |
Never cache: cart, checkout, account pages. Always cache: product pages, category pages.
All state-changing requests (POST, DELETE) must validate CSRF tokens. The token is generated in the controller, passed to the ISML template as a hidden form field, and validated on submission via csrfProtection.validateRequest middleware.
Server-side form validation uses server.forms.getForm('formName'). Form definitions live in forms/default/*.xml. Always validate server-side -- never trust client-side validation alone.
Product-Show (view), Cart-AddProduct (action)server.use for method-specific routesscripts/ helpersnext() -- forgetting breaks the chain silentlyscripts/middleware/server.middleware.https for all sensitive operations{ success: boolean, data?: object, errors?: array }res.setViewData / res.getViewData to share data across middleware chainreq.querystring, req.form)Transaction.wrap()dw/system/LoggerFetch the SFRA GitHub repository and server module API reference for exact method signatures, middleware patterns, and req/res property details before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.