references: https://shopify.dev/docs/storefronts/headless/hydrogen/fundamentals Hydrogen and Oxygen make up Shopify’s recommended stack for headless commerce. The different parts of the system work together to make it faster and easier to build and deploy headless Shopify stores. Anchor to Architecture Architecture Three key parts of the Hydrogen stack work together to enable a unified developer experience: Technology What it does Hydrogen (App) A set of components, utilities, and design patterns that make it easier to work with Shopify APIs. Hydrogen projects are React Router apps that are preconfigured with Shopify-specific features and functionality. Hydrogen handles API client credentials, provides off-the-shelf components that are pre-wired for Shopify API data, and includes CLI tooling for local development, testing, and deployment. React Router (Framework) The open-source React framework that Hydrogen is built on top of. React Router handles routing, data fetching, server-side rendering, UI reactivity, and styling. Oxygen (Hosting) Shopify’s global serverless hosting platform, built for deploying Hydrogen storefronts at the edge. Oxygen handles deployment environments, environment variable management, caching, and integration with Shopify’s CDN. Developing each layer of this tech stack together means provides an end-to-end developer experience that reduces boilerplate code, improves productivity, and promotes optimal performance, accessibility, and SEO practices. Anchor to HydrogenHydrogen Anchor to Project structureProject structure Hydrogen projects are structured like typical React Router apps and you can configure them to your preferences. The following is the default Quickstart project structure: Hydrogen project structure 📂 hydrogen-quickstart/ ├── 📁 app/ │ ├── 📁 assets/ │ ├── 📁 components/ │ ├── 📁 graphql/ │ ├── 📁 lib/ │ ├── 📁 routes/ │ ├── 📁 styles/ │ ├── entry.client.jsx │ ├── entry.server.jsx │ └── root.jsx ├── 📁 public/ ├── CHANGELOG.md ├── README.md ├── customer-accountapi.generated.d.ts ├── env.d.ts ├── jsconfig.json ├── package.json ├── postcss.config.js ├── server.js ├── storefrontapi.generated.d.ts └── vite.config.js Anchor to Packages and dependencies Packages and dependencies Hydrogen bundles a set of dependencies that work together to enable end-to-end development and deployment: Package Description @shopify/hydrogen Main Hydrogen package. Contains components specific to React Router and utilities for interacting with Shopify APIs. Extends the framework-agnostic @shopify/hydrogen-react package. @shopify/hydrogen-cli CLI tool for working with Hydrogen projects. @shopify/mini-oxygen Local development server based on Oxygen. @shopify/remix-oxygen Remix adapter that enables Hydrogen to be served on Oxygen. Anchor to Hydrogen channelHydrogen channel The Hydrogen sales channel app needs to be installed on your Shopify store to enable the following features: A Hydrogen sales channel where you can publish product inventory. Oxygen hosting, to deploy your Hydrogen projects. Managing storefronts and deployment environments, including environment variable management. Access to deployment logs. Anchor to RoutesRoutes The standard format for product URLs is /products/:handle. If your storefront uses a different structure, then it’s recommended that you provide a server-side redirect (3XX) from the expected /products/:handle path to the product page. It’s also recommended that your storefront supports cart permalinks. View example implementation Anchor to React RouterReact Router React Router is the open-source React-based framework that Hydrogen is built on top of. Hydrogen projects are React Router apps with a set of preconfigured options, bundled with a collection of Shopify-optimized components and utilities. Hydrogen includes a custom React Router adapter that compiles your project for hosting on Oxygen. Tip Consider completing React Router’s 30-minute getting started tutorial for a solid foundation on the architecture and conventions of React Router apps. Anchor to Key React Router concepts Key React Router concepts Concept Details Nested routes React Router maps the nesting logic of app URLs to the nesting logic of components and data-loading. This allows all page data to load in parallel, reducing overall load times. Loaders React Router loaders are functions that load data so that it can be rendered server-side, which reduces the amount of JavaScript that’s sent to the client. In Hydrogen, loaders fetch data from Shopify APIs and third-party sources. Actions React Router actions are functions that accept web-standard form data from clients in order to update state, mutate data, or trigger side effects. SSR React Router apps default to server-side rendering (SSR), where their React components are rendered as HTML before being sent to the browser. Progressive enhancement Because React Router actions use web standard technology like HTML forms, they typically work without JavaScript, but can be enhanced with client-side JavaScript when it’s available. This, along with an SSR-first approach, means React Router apps typically deliver smaller bundle sizes that load faster. Anchor to Oxygen Oxygen Oxygen is Shopify’s global deployment platform that’s built for hosting Hydrogen storefronts at the edge. It provides multiple deployment environments, so you can preview every change before shipping it to production. Oxygen supports continuous deployment using GitHub, or you can configure your own custom CI/CD system. Enable access to Oxygen by installing the Hydrogen channel. Anchor to Supported plansSupported plans Oxygen is available at no extra charge on paid Shopify plans: Pause and build Basic Shopify Advanced Plus Oxygen isn’t available on Starter plans or development stores. Anchor to Technical specsTechnical specs Oxygen is a worker-based JavaScript runtime, based on Cloudflare’s open-source workerd library. It supports web standard APIs such as Fetch, Cache, Streams, Web Crypto, and more. Some Node.js APIs aren’t available. Check the Oxygen runtime details for a complete list. If you prefer, you can self-host Hydrogen. Anchor to LimitationsLimitations You can use Oxygen for hosting commerce storefronts. It’s subject to the Shopify Acceptable Use Policy. Misuse or abuse of Oxygen might lead to throttling, suspension, or termination. Workers: Must be 10 MB or less The startup time (the duration it takes for the worker to begin processing requests) must be 400 milliseconds or less. Must be named index.js. The optional source map file must be named index.js.map. Are limited to 30 seconds of CPU time per request Can consume 128 MB max of memory. Exceeding this limit could mean dropped requests. Are limited to 110 custom environment variables Outbound API requests must complete within 2 minutes Static assets, maximum file sizes: Images: 20 MB Video: 1 GB 3D models: 500 MB Other files: 20 MB Caution Ensure your requests go directly to Oxygen. Because proxies can conflict with Oxygen’s bot mitigation systems and cause SEO issues, Oxygen doesn’t support proxies in front of your Oxygen deployments. Anchor to Next stepsNext steps Fetch product data from Shopify
Headless just refers to separating frontend (presentation) from backend (storage), something done in enterprise CMS systems for decades. In traditional CMSs like Drupal, WordPress, etc. content storage and display code and combined creating content reuse, workflow and automation difficulties. Also creates content silos by storing content in particular CMS’s database schema. A headless CMS allows you to manage content in one place and be able to deploy that content on any digital channel you choose. How to do it: Headless CMS includes only the back-end functionalities (creation and storage of your content) and makes use of REST API or GraphQL API to display content on the front-end. This gives the opportunity to make use of modern web technologies for the front-end, while the back-end makes use of a dedicated content management system.
First off, we need to understand what is meant by the term ‘platform’ for the organization. In todays world, the term ‘platform’ is such an overused buzzword that it is largely meaningless. Platforms exist at many levels in the technology stack: hardware, datacenter, database, messaging, etc. and can also be used to describe ecosystems of user experiences. The view presented here is decidedly business focused: the ‘platform’ is really the method of exposing and growing the digital business model and its offerings. So, the following ground rules define the platform as the digital server-side, back-end data and functionality, leaving the user experiences to be crafted (whether mobile, web or other) on top of that business functionality. This API-centric definition of ‘platform’ enables: Rapid user experience revision Consistent user experience across devices Better channel adaptation Faster and cheaper third-party integrations Increased innovation capabilities. Platform Ground Rules Mobile applications are a first-class citizen and consumer of the platform. Making mobile a priority increases ease of use and adoption for everyone. The platform exposes its data and functionality via a complete set of RESTful APIs. An API-based strategy maximizes reuse and flexibility. All service interfaces and events are designed from the ground up to be public facing. Causes design for ease of use, flexibility and reuse. Each team of developers access all APIs using their team’s own registered developer key. Improves security, auditability. There is a single token source and format for all APIs. Assists in ease of use and ensures all functionality is available to consumers. The platform will notify interested observers of all resource state changes via fully-composed events. Enables synchronization asynchronously (for all create, update, delete state changes). All APIs use a single identity to represent the consumer / user. Enables analytics and reasoning about consumers. All APIs have a consistent look and feel, and are documented at a single, publicly-available location. Maximizes reuse and ease of use. No private interfaces. No backdoors. No shared databases across teams (Observe ‘Bounded Contexts’). Ensures the public interfaces are sound. APIs are deployed in a distributed, always-on architecture (multi-node, multi-zone, and multi-region) with a discipline towards resilience. Horizontal scale on commodity hardware, global reach. Account for the inevitable failures.
Do only one thing and do it well. The “one thing” is defined by a “Bounded Context” in Domain-Driven Design (DDD). Own your own data. No shared data stores. Embrace eventual consistency. Don’t read your writes. Publish your own state-changes (minimally) to an event log. Leverage event logging and/or streaming to replicate and denormalize data from other services. The event log must be subscribable to publish events to subscribers. The event log must be durable and must not lose messages for publishing and replay. Examples: Kafka, Confluent.io, Amazon Kinesis. When aggregating microservice calls, use asynchronous, non-blocking I/O. Perform as much as possible asynchronously. Support a registry/discovery mechanism. Examples: Consul, Eureka Support Location Transparency. Support statelessness–no sticky sessions. Support the use of back-pressure (see, Reactive Streams specification) to avoid cascading failures. Examples: RxJava Support the Circuit Breaker pattern to manage faulty service dependencies. Examples: Hystrix, Apache Zest Consider CQRS to scale reads separately from writes.
Build the API with consumers in mind–as a product in its own right. Not for a specific UI. Embrace flexibility / tunability of each endpoint (see #5, 6 & 7). Eat your own dogfood, even if you have to mockup an example UI. Use the Collection Metaphor. Two URLs (endpoints) per resource: The resource collection (e.g. /orders) Individual resource within the collection (e.g. /orders/{orderId}). Use plural forms (‘orders’ instead of ‘order’). Alternate resource names with IDs as URL nodes (e.g. /orders/{orderId}/items/{itemId}) Keep URLs as short as possible. Preferably, no more-than three nodes per URL. Use nouns as resource names (e.g. don’t use verbs in URLs). Make resource representations meaningful. “No Naked IDs!” No plain IDs embedded in responses. Use links and reference objects. Design resource representations. Don’t simply represent database tables. Merge representations. Don’t expose relationship tables as two IDs. Support filtering, sorting, and pagination on collections. Support link expansion of relationships. Allow clients to expand the data contained in the response by including additional representations instead of, or in addition to, links. Support field projections on resources. Allow clients to reduce the number of fields that come back in the response. Use the HTTP method names to mean something: POST – create and other non-idempotent operations. PUT – update. GET – read a resource or collection. DELETE – remove a resource or collection. Use HTTP status codes to be meaningful. 200 – Success. 201 – Created. Returned on successful creation of a new resource. Include a ‘Location’ header with a link to the newly-created resource. 400 – Bad request. Data issues such as invalid JSON, etc. 404 – Not found. Resource not found on GET. 409 – Conflict. Duplicate data or invalid data state would occur. Use ISO 8601 timepoint formats for dates in representations. Consider connectedness by utilizing a linking strategy. Some popular examples are: HAL Siren JSON-LD Collection+JSON Use OAuth2 to secure your API. Use a Bearer token for authentication. Require HTTPS / TLS / SSL to access your APIs. OAuth2 Bearer tokens demand it. Unencrypted communication over HTTP allows for simple eavesdroppping and impersonation. Use Content-Type negotiation to describe incoming request payloads. For example, let’s say you’re doing ratings, including a thumbs-up/thumbs-down and five-star rating. You have one route to create a rating: POST /ratings How do you distinguish the incoming data to the service so it can determine which rating type it is: thumbs-up or five star? The temptation is to create one route for each rating type: POST /ratings/five_star and POST /ratings/thumbs_up However, by using Content-Type negotiation we can use our same POST /ratings route for both types. By setting the Content-Type header on the request to something like Content-Type: application/vnd.company.rating.thumbsup or Content-Type: application/vnd.company.rating.fivestar the server can determine how to process the incoming rating data. Evolution over versioning. However, if versioning, use the Accept header instead of versioning in the URL. Versioning via the URL signifies a ‘platform’ version and the entire platform must be versioned at the same time to enable the linking strategy. Versioning via the Accept header is versioning the resource. Additions to a JSON response do not require versioning. However, additions to a JSON request body that are ‘required’ are troublesome–and may require versioning. Hypermedia linking and versioning is troublesome no matter what–minimize it. Note that a version in the URL, while discouraged, can be used as a ‘platform’ version. It should appear as the first node in the path and not version individual endpoints differently (e.g. api.example.com/v1/…). Consider Cache-ability. At a minimum, use the following response headers: ETag – An arbitrary string for the version of a representation. Make sure to include the media type in the hash value, because that makes a different representation. (ex: ETag: “686897696a7c876b7e”) Date – Date and time the response was returned (in RFC1123 format). (ex: Date: Sun, 06 Nov 1994 08:49:37 GMT) Cache-Control – The maximum number of seconds (max age) a response can be cached. However, if caching is not supported for the response, then no-cache is the value. (ex: Cache-Control: 360 or Cache-Control: no-cache) Expires – If max age is given, contains the timestamp (in RFC1123 format) for when the response expires, which is the value of Date (e.g. now) plus max age. If caching is not supported for the response, this header is not present. (ex: Expires: Sun, 06 Nov 1994 08:49:37 GMT) Pragma – When Cache-Control is ‘no-cache’ this header is also set to ‘no-cache’. Otherwise, it is not present. (ex: Pragma: no-cache) Last-Modified – The timestamp that the resource itself was modified last (in RFC1123 format). (ex: Last-Modified: Sun, 06 Nov 1994 08:49:37 GMT) Ensure that your GET, PUT, and DELETE operations are all idempotent. There should be no adverse side affects from operations.