App proxyobject
App proxies take requests to Shopify links, and redirect them to external links.
The function validates requests made to app proxies, and returns a context to enable querying Shopify APIs.
If the store has not installed the app, store-related properties such as admin
or storefront
will be undefined
Authenticates requests coming to the app from Shopify app proxies.
- Anchor to requestrequestRequestrequired
AuthenticateAppProxy
- request
Request
Promise<AppProxyContext | AppProxyContextWithSession>
(
request: Request,
) => Promise<AppProxyContext | AppProxyContextWithSession>
AppProxyContext
- admin
No session is available for the shop that made this request. Therefore no methods for interacting with the GraphQL / REST Admin APIs are available.
undefined
- liquid
A utility for creating a Liquid Response.
LiquidResponseFunction
- session
No session is available for the shop that made this request. This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.
undefined
- storefront
No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available.
undefined
export interface AppProxyContext extends Context {
/**
* No session is available for the shop that made this request.
*
* This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.
*/
session: undefined;
/**
* No session is available for the shop that made this request.
* Therefore no methods for interacting with the GraphQL / REST Admin APIs are available.
*/
admin: undefined;
/**
* No session is available for the shop that made this request.
* Therefore no method for interacting with the Storefront API is available.
*/
storefront: undefined;
}
LiquidResponseFunction
- body
string
- initAndOptions
number | (ResponseInit & Options)
Response
(
body: string,
initAndOptions?: number | (ResponseInit & Options),
) => Response
Options
- layout
Whether to use the shop's theme layout around the Liquid content.
boolean
interface Options {
/**
* Whether to use the shop's theme layout around the Liquid content.
*/
layout?: boolean;
}
AppProxyContextWithSession
- admin
Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.
AdminApiContext
- liquid
A utility for creating a Liquid Response.
LiquidResponseFunction
- session
The session for the shop that made the request. This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice. Use this to get shop or user-specific data.
Session
- storefront
Method for interacting with the Shopify Storefront Graphql API for the store that made the request.
StorefrontContext
export interface AppProxyContextWithSession extends Context {
/**
* The session for the shop that made the request.
*
* This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.
*
* Use this to get shop or user-specific data.
*
* @example
* <caption>Using the session object.</caption>
* <description>Get the session for the shop that initiated the request to the app proxy.</description>
* ```ts
* // app/routes/**\/.ts
* import { json } from "react-router";
* import { authenticate } from "../shopify.server";
* import { getMyAppModelData } from "~/db/model.server";
*
* export const loader = async ({ request }) => {
* // Get the session for the shop that initiated the request to the app proxy.
* const { session } =
* await authenticate.public.appProxy(request);
*
* // Use the session data to make to queries to your database or additional requests.
* return (
* await getMyAppModelData({shop: session.shop})
* );
* };
* ```
*/
session: Session;
/**
* Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.
*
* @example
* <caption>Interacting with the Admin API.</caption>
* <description>Use the `admin` object to interact with the admin GraphQL API.</description>
* ```ts
* // app/routes/**\/.ts
* import { json } from "react-router";
* import { authenticate } from "../shopify.server";
*
* export async function action({ request }: ActionFunctionArgs) {
* const { admin } = await authenticate.public.appProxy(request);
*
* const response = await admin.graphql(
* `#graphql
* mutation populateProduct($input: ProductInput!) {
* productCreate(input: $input) {
* product {
* id
* }
* }
* }`,
* {
* variables: {
* input: { title: "Product Name" }
* }
* }
* );
*
* const productData = await response.json();
* return ({ data: productData.data });
* }
* ```
*/
admin: AdminApiContext;
/**
* Method for interacting with the Shopify Storefront Graphql API for the store that made the request.
*
* @example
* <caption>Interacting with the Storefront API.</caption>
* <description>Use the `storefront` object to interact with the GraphQL API.</description>
* ```ts
* // app/routes/**\/.ts
* import { json } from "react-router";
* import { authenticate } from "../shopify.server";
*
* export async function action({ request }: ActionFunctionArgs) {
* const { storefront } = await authenticate.public.appProxy(request);
*
* const response = await storefront.graphql(
* `#graphql
* query blogIds {
* blogs(first: 10) {
* edges {
* node {
* id
* }
* }
* }
* }`
* );
*
* return (await response.json());
* }
* ```
*/
storefront: StorefrontContext;
}
AdminApiContext
- graphql
Methods for interacting with the Shopify Admin GraphQL API
GraphQLClient<AdminOperations>
export interface AdminApiContext {
/**
* Methods for interacting with the Shopify Admin GraphQL API
*
* {@link https://shopify.dev/docs/api/admin-graphql}
* {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}
*
* @example
* <caption>Querying the GraphQL API.</caption>
* <description>Use `admin.graphql` to make query / mutation requests.</description>
* ```ts
* // /app/routes/**\/*.ts
* import { ActionFunctionArgs } from "react-router";
* import { authenticate } from "../shopify.server";
*
* export const action = async ({ request }: ActionFunctionArgs) => {
* const { admin } = await authenticate.admin(request);
*
* const response = await admin.graphql(
* `#graphql
* mutation populateProduct($input: ProductInput!) {
* productCreate(input: $input) {
* product {
* id
* }
* }
* }`,
* {
* variables: {
* input: { title: "Product Name" },
* },
* },
* );
*
* const productData = await response.json();
* return ({
* productId: productData.data?.productCreate?.product?.id,
* });
* }
* ```
*
* ```ts
* // /app/shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-react-router/server";
*
* const shopify = shopifyApp({
* // ...
* });
* export default shopify;
* export const authenticate = shopify.authenticate;
* ```
*
* @example
* <caption>Handling GraphQL errors.</caption>
* <description>Catch `GraphqlQueryError` errors to see error messages from the API.</description>
* ```ts
* // /app/routes/**\/*.ts
* import { ActionFunctionArgs } from "react-router";
* import { authenticate } from "../shopify.server";
*
* export const action = async ({ request }: ActionFunctionArgs) => {
* const { admin } = await authenticate.admin(request);
*
* try {
* const response = await admin.graphql(
* `#graphql
* query incorrectQuery {
* products(first: 10) {
* nodes {
* not_a_field
* }
* }
* }`,
* );
*
* return ({ data: await response.json() });
* } catch (error) {
* if (error instanceof GraphqlQueryError) {
* // error.body.errors:
* // { graphQLErrors: [
* // { message: "Field 'not_a_field' doesn't exist on type 'Product'" }
* // ] }
* return ({ errors: error.body?.errors }, { status: 500 });
* }
* return ({ message: "An error occurred" }, { status: 500 });
* }
* }
* ```
*
* ```ts
* // /app/shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-react-router/server";
*
* const shopify = shopifyApp({
* // ...
* });
* export default shopify;
* export const authenticate = shopify.authenticate;
* ```
*/
graphql: GraphQLClient<AdminOperations>;
}
GraphQLClient
- query
Operation extends keyof Operations
- options
GraphQLQueryOptions<Operation, Operations>
interface Promise<T> {
/**
* Attaches callbacks for the resolution and/or rejection of the Promise.
* @param onfulfilled The callback to execute when the Promise is resolved.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of which ever callback is executed.
*/
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;
/**
* Attaches a callback for only the rejection of the Promise.
* @param onrejected The callback to execute when the Promise is rejected.
* @returns A Promise for the completion of the callback.
*/
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}, interface Promise<T> {}, Promise: PromiseConstructor, interface Promise<T> {
readonly [Symbol.toStringTag]: string;
}, interface Promise<T> {
/**
* Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
* resolved value cannot be modified from the callback.
* @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
* @returns A Promise for the completion of the callback.
*/
finally(onfinally?: (() => void) | undefined | null): Promise<T>;
}
<
Operation extends keyof Operations,
>(
query: Operation,
options?: GraphQLQueryOptions<Operation, Operations>,
) => Promise<GraphQLResponse<Operation, Operations>>
GraphQLQueryOptions
- apiVersion
The version of the API to use for the request.
ApiVersion
- headers
Additional headers to include in the request.
Record<string, any>
- signal
An optional AbortSignal to cancel the request.
AbortSignal
- tries
The total number of times to try the request if it fails.
number
- variables
The variables to pass to the operation.
ApiClientRequestOptions<Operation, Operations>["variables"]
export interface GraphQLQueryOptions<
Operation extends keyof Operations,
Operations extends AllOperations,
> {
/**
* The variables to pass to the operation.
*/
variables?: ApiClientRequestOptions<Operation, Operations>['variables'];
/**
* The version of the API to use for the request.
*/
apiVersion?: ApiVersion;
/**
* Additional headers to include in the request.
*/
headers?: Record<string, any>;
/**
* The total number of times to try the request if it fails.
*/
tries?: number;
/**
* An optional AbortSignal to cancel the request.
*/
signal?: AbortSignal;
}
StorefrontContext
- graphql
Method for interacting with the Shopify Storefront GraphQL API If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-react-router/tree/main#incorrect-graphql-hints).
GraphQLClient<StorefrontOperations>
export interface StorefrontContext {
/**
* Method for interacting with the Shopify Storefront GraphQL API
*
* If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-react-router/tree/main#incorrect-graphql-hints).
*
* {@link https://shopify.dev/docs/api/storefront}
*
* @example
* <caption>Querying the GraphQL API.</caption>
* <description>Use `storefront.graphql` to make query / mutation requests.</description>
* ```ts
* // app/routes/**\/.ts
* import { json } from "react-router";
* import { authenticate } from "../shopify.server";
*
* export async function action({ request }: ActionFunctionArgs) {
* const { storefront } = await authenticate.public.appProxy(request);
*
* const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);
*
* return (await response.json());
* }
* ```
*
* @example
* <caption>Handling GraphQL errors.</caption>
* <description>Catch `GraphqlQueryError` errors to see error messages from the API.</description>
* ```ts
* // /app/routes/**\/*.ts
* import { ActionFunctionArgs } from "react-router";
* import { authenticate } from "../shopify.server";
*
* export const action = async ({ request }: ActionFunctionArgs) => {
* const { storefront } = await authenticate.public.appProxy(request);
*
* try {
* const response = await storefront.graphql(
* `#graphql
* query incorrectQuery {
* products(first: 10) {
* nodes {
* not_a_field
* }
* }
* }`,
* );
*
* return ({ data: await response.json() });
* } catch (error) {
* if (error instanceof GraphqlQueryError) {
* // { errors: { graphQLErrors: [
* // { message: "Field 'not_a_field' doesn't exist on type 'Product'" }
* // ] } }
* return ({ errors: error.body?.errors }, { status: 500 });
* }
* return ({ message: "An error occurred" }, { status: 500 });
* }
* }
* ```
*
* ```ts
* // /app/shopify.server.ts
* import { shopifyApp } from "@shopify/shopify-app-react-router/server";
*
* const shopify = shopifyApp({
* // ...
* });
* export default shopify;
* export const authenticate = shopify.authenticate;
* ```
*/
graphql: GraphQLClient<StorefrontOperations>;
}
Authenticate and fetch product information
/app/routes/**.ts
examples
Authenticate and fetch product information
description
Authenticate and fetch product information
/app/routes/**.ts
import type {LoaderFunctionArgs} from 'react-router'; import {authenticate} from '../shopify.server'; export const loader = async ({request}: LoaderFunctionArgs) => { const {storefront, liquid} = await authenticate.public.appProxy(request); if (!storefront) { return new Response(); } const response = await storefront.graphql( `#graphql query productTitle { products(first: 1) { nodes { title } } }`, ); const body = await response.json(); const title = body.data.products.nodes[0].title; return liquid(`Found product ${title} from {{shop.name}}`); };
Anchor to examplesExamples
Anchor to example-interacting-with-the-admin-apiInteracting with the Admin API
Use the admin
object to interact with the admin GraphQL API.
Interacting with the Admin API
app/routes/**\/.ts
examples
Interacting with the Admin API
description
Use the `admin` object to interact with the admin GraphQL API.
app/routes/**\/.ts
import { json } from "react-router"; import { authenticate } from "../shopify.server"; export async function action({ request }: ActionFunctionArgs) { const { admin } = await authenticate.public.appProxy(request); const response = await admin.graphql( `#graphql mutation populateProduct($input: ProductInput!) { productCreate(input: $input) { product { id } } }`, { variables: { input: { title: "Product Name" } } } ); const productData = await response.json(); return ({ data: productData.data }); }
Anchor to example-liquidliquid
Anchor to example-rendering-liquid-contentRendering liquid content
Use the liquid
helper to render a Response
with Liquid content using the shop's theme. See the Liquid reference for all the features it enables.
Anchor to example-rendering-liquid-content-without-a-layoutRendering liquid content without a layout
Set the layout
option to false
to render the Liquid content without a theme.
Anchor to example-rendering-a-form-in-a-liquid-responseRendering a form in a Liquid response
Handle form submissions through an app proxy.
Rendering liquid content
/app/routes/**\/*.ts
examples
Rendering liquid content
description
Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables.
/app/routes/**\/*.ts
import {authenticate} from "~/shopify.server" export async function loader({ request }) { const {liquid} = await authenticate.public.appProxy(request); return liquid("Hello {{shop.name}}"); }
Rendering liquid content without a layout
description
Set the `layout` option to `false` to render the Liquid content without a theme.
/app/routes/**\/*.ts
import {authenticate} from "~/shopify.server" export async function loader({ request }) { const {liquid} = await authenticate.public.appProxy(request); return liquid( "Hello {{shop.name}}", { layout: false } ); }
Rendering a form in a Liquid response
description
Handle form submissions through an app proxy.
app/routes/apps.proxy.my-action.tsx
import { redirect } from "react-router"; import { authenticate } from "~/shopify.server"; export async function loader({ request }) { const { liquid } = await authenticate.public.appProxy(request); return liquid(` <form method="post" action="/apps/proxy/my-action"> <input type="text" name="field" /> <button type="submit">Submit</button> </form> `); } export async function action({ request }) { await authenticate.public.appProxy(request); const formData = await request.formData(); const field = formData.get("field")?.toString(); // Perform actions here if (field) { console.log("Field:", field); } // Return to the form page return redirect("/apps/proxy/my-action"); }
Anchor to example-sessionsession
Anchor to example-using-the-session-objectUsing the session object
Get the session for the shop that initiated the request to the app proxy.
Using the session object
app/routes/**\/.ts
examples
Using the session object
description
Get the session for the shop that initiated the request to the app proxy.
app/routes/**\/.ts
import { json } from "react-router"; import { authenticate } from "../shopify.server"; import { getMyAppModelData } from "~/db/model.server"; export const loader = async ({ request }) => { // Get the session for the shop that initiated the request to the app proxy. const { session } = await authenticate.public.appProxy(request); // Use the session data to make to queries to your database or additional requests. return ( await getMyAppModelData({shop: session.shop}) ); };
Anchor to example-storefrontstorefront
Anchor to example-interacting-with-the-storefront-apiInteracting with the Storefront API
Use the storefront
object to interact with the GraphQL API.
Interacting with the Storefront API
app/routes/**\/.ts
examples
Interacting with the Storefront API
description
Use the `storefront` object to interact with the GraphQL API.
app/routes/**\/.ts
import { json } from "react-router"; import { authenticate } from "../shopify.server"; export async function action({ request }: ActionFunctionArgs) { const { storefront } = await authenticate.public.appProxy(request); const response = await storefront.graphql( `#graphql query blogIds { blogs(first: 10) { edges { node { id } } } }` ); return (await response.json()); }