Building Shopify app extensions
Working with Admin and Checkout UI extensions
Setting up your extension app from scratch
- In your local terminal run the following command to create a new Shopify CLI app:
npm init @shopify/app@latest
yarn create @shopify/app
- Select the
Start by adding your first extension
option. - Upon app creation open the generated app folder and
cd
into the app's root. - Then run the following command to generate your extension.
npm run generate extension
yarn generate extension
- Select
Yes, create it as a new app
- Proceed to name your app.
- You will then be prompted to select the type of admin extension or checkout UI extension you wish to build and to name it.
- After the extension code is generated open your
shopify.app.toml
file, locate theaccess_scopes
section and add the following to it:
shopify.app.toml[access_scopes]# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopesscopes = ""use_legacy_install_flow = true
- After updating the TOML file, push the configuration changes to your partner's app using the following command:
terminalshopify app config push
- Finally head over to Gadget, create a new Shopify app and connect to Shopify (instead of creating a manual app from the Partners dashboard you will use the app you created earlier through the CLI).
Setting up your extension with an existing Shopify connected Gadget app
Alternatively if you already have Gadget app with an established Shopify connection you can proceed to create a new Shopify CLI app and select the Start by adding your first extension
option.
Follow along with the rest of the extension setup, but make sure as prompted by the CLI you link your extension to the same Partners app you used to set up your Shopify connection in Gadget, and select the same development store you installed your app on.
Accessing your Gadget backend data from the extension
How Auth is handeled in Admin extensions
Gadget backends automatically support authenticating requests made from within Admin Extensions to your Gadget backend.
Shopify automatically adds an Authorization header to requests sent by Admin Extensions containing a Shopify Session Token. Similar to embedded applications authenticating against your Gadget backend, Gadget will parse the token contained in this header and set up a record in your session model representing a given merchant, as well as set up the connections.shopify
object for communicating with with Shopify Admin API.
Using the API client in Extensions
We recommend using our API client within the extension to get data access as it is the easiest way to get started:
cd
into your extension code's directory.- Install the client.
- Once your client is installed, you must provide an instance of your client using the Gadget React provider.
- Import the api instance and the provider, and wrap the provider around the
reactExtension
code as below:
Checkout.jsxJavaScript1import { Banner, reactExtension } from "@shopify/ui-extensions-react/checkout";2import { Provider } from "@gadgetinc/react"; /// Imported provider3import { api } from "../api"; // Importing the instance created45export default reactExtension("purchase.checkout.block.render", () => (6 <Provider api={api}>7 {/* Wrapping the extension around the Gadget provider and passing the api instance */}8 <Extension />9 </Provider>10));1112function Extension() {13 return <Banner>This is a product.</Banner>;14}
- Now your extension has full access to the Gadget API and all the react hooks, so below will simply get data from our Gadget backend using a
useFindOne
hook to grab the title of ourshopifyProduct
model.
Checkout.jsxJavaScript1import { Banner, reactExtension } from "@shopify/ui-extensions-react/checkout";2import { Provider, useFindOne } from "@gadgetinc/react";3import { api } from "../api";45export default reactExtension("purchase.checkout.block.render", () => (6 <Provider api={api}>7 <Extension />8 </Provider>9));1011function Extension() {12 const [{ data, error, fetching }, refresh] = useFindOne(api.shopifyProduct, "123");1314 if (fetching) {15 return <Banner>Loading...</Banner>;16 }1718 if (error) {19 return <Banner>Error loading product. Please try again.</Banner>;20 }2122 return <Banner>{data.title}</Banner>;23}
HTTP request using the GraphQL endpoint
Alternatively as a fallback option you can also make a request directly to fetch from your GraphQl API endpoint.
Gadget supports authenticating requests made from Shopify Checkout Extensions using Shopify's Session Token feature. Inside a checkout extension that has the network_access
capability, you can make a request to your backend Gadget API or an HTTP route:
Checkout.jsxJavaScript1import {2 Banner,3 reactExtension,4 useApi,5} from "@shopify/ui-extensions-react/checkout";6import { useState, useEffect } from "react";78export default reactExtension("purchase.checkout.block.render", () => <Extension />);910function Extension() {11 // Shopify's Session Token12 const { sessionToken } = useApi();13 const [productData, setProductData] = useState(null);1415 useEffect(() => {16 // Specify the GraphQL endpoint17 const url = "https://my-extension-app-development.gadget.dev/api/graphql";1819 // Create a GraphQL query20 const query = `21 query GetOneShopifyProduct($id: GadgetID!) {22 shopifyProduct(id: $id) {23 title24 }25 }26 `;2728 // Fetching the Token29 async function getToken() {30 const token = await sessionToken.get();31 return token;32 }3334 // fetch and make a POST request to the GraphQL endpoint35 getToken().then((token) => {36 fetch(url, {37 method: "POST",38 headers: {39 "Content-Type": "application/json",40 Accept: "application/json",41 Authorization: `Bearer ${token}`,42 },43 body: JSON.stringify({ query: query }),44 })45 .then((response) => response.json())46 .then((data) => {47 setProductData(data.data.product);48 })49 .catch((error) => console.error("Error:", error));50 });51 }, []);5253 return <Banner>{productData.title}</Banner>;54}
Using Metafields
Another option which is a bit lengthier involves querying and writing metafields to pass and set data within extension.
For more information checkout our metafields guide.