# App proxy [App proxies](https://shopify.dev/docs/apps/online-store/app-proxies) take requests to Shopify links, and redirect them to external links. The `authenticate.public.appProxy` function validates requests made to app proxies, and returns a context to enable querying Shopify APIs. > Note: If the store has not installed the app, store-related properties such as `admin` or `storefront` will be `undefined` ```typescript import type {LoaderFunctionArgs} from '@remix-run/node'; 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}}`); }; ``` ## authenticate.public.appProxy Authenticates requests coming to the app from Shopify app proxies. ### AuthenticateAppProxy #### Returns: Promise< AppProxyContext | AppProxyContextWithSession > #### Params: - request: Request export type AuthenticateAppProxy< ConfigArg extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > = ( request: Request, ) => Promise< AppProxyContext | AppProxyContextWithSession >; ### AppProxyContext ### admin value: `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. ### liquid value: `LiquidResponseFunction` - LiquidResponseFunction: export type LiquidResponseFunction = ( body: string, initAndOptions?: number | (ResponseInit & Options), ) => Response; A utility for creating a Liquid Response. ### session value: `undefined` 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. ### storefront value: `undefined` No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available. ### LiquidResponseFunction #### Returns: Response #### Params: - body: string - initAndOptions: number | (ResponseInit & Options) export type LiquidResponseFunction = ( body: string, initAndOptions?: number | (ResponseInit & Options), ) => Response; ### Options ### layout value: `boolean` Whether to use the shop's theme layout around the Liquid content. ### AppProxyContextWithSession ### admin value: `AdminApiContext` Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request. ### liquid value: `LiquidResponseFunction` - LiquidResponseFunction: export type LiquidResponseFunction = ( body: string, initAndOptions?: number | (ResponseInit & Options), ) => Response; A utility for creating a Liquid Response. ### session value: `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. ### storefront value: `StorefrontContext` - StorefrontContext: 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-remix/tree/main#incorrect-graphql-hints). * * {@link https://shopify.dev/docs/api/storefront} * * @example * Querying the GraphQL API. * Use `storefront.graphql` to make query / mutation requests. * ```ts * // app/routes/**\/.ts * import { json } from "@remix-run/node"; * 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 json(await response.json()); * } * ``` * * @example * Handling GraphQL errors. * Catch `GraphqlQueryError` errors to see error messages from the API. * ```ts * // /app/routes/**\/*.ts * import { ActionFunctionArgs } from "@remix-run/node"; * 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 json({ data: await response.json() }); * } catch (error) { * if (error instanceof GraphqlQueryError) { * // { errors: { graphQLErrors: [ * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" } * // ] } } * return json({ errors: error.body?.errors }, { status: 500 }); * } * return json({ message: "An error occurred" }, { status: 500 }); * } * } * ``` * * ```ts * // /app/shopify.server.ts * import { shopifyApp } from "@shopify/shopify-app-remix/server"; * * const shopify = shopifyApp({ * // ... * }); * export default shopify; * export const authenticate = shopify.authenticate; * ``` */ graphql: GraphQLClient; } Method for interacting with the Shopify Storefront Graphql API for the store that made the request. ### StorefrontContext ### graphql value: `GraphQLClient` - GraphQLClient: export type GraphQLClient = < Operation extends keyof Operations, >( query: Operation, options?: GraphQLQueryOptions, ) => Promise>; 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-remix/tree/main#incorrect-graphql-hints). ### GraphQLClient #### Returns: interface Promise { /** * 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(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise; /** * 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(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise; }, interface Promise {}, Promise: PromiseConstructor, interface Promise { readonly [Symbol.toStringTag]: string; }, interface Promise { /** * 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; } #### Params: - query: Operation extends keyof Operations - options: GraphQLQueryOptions export type GraphQLClient = < Operation extends keyof Operations, >( query: Operation, options?: GraphQLQueryOptions, ) => Promise>; ### GraphQLQueryOptions ### apiVersion value: `ApiVersion` The version of the API to use for the request. ### headers value: `Record` Additional headers to include in the request. ### signal value: `AbortSignal` An optional AbortSignal to cancel the request. ### tries value: `number` The total number of times to try the request if it fails. ### variables value: `ApiClientRequestOptions["variables"]` - Options: interface Options { /** * Whether to use the shop's theme layout around the Liquid content. */ layout?: boolean; } The variables to pass to the operation. ## Related - [Admin API context](https://shopify.dev/docs/api/shopify-app-remix/apis/admin-api) - [Storefront API context](https://shopify.dev/docs/api/shopify-app-remix/apis/storefront-api) - [Liquid reference](https://shopify.dev/docs/api/liquid) ## Examples [App proxies](https://shopify.dev/docs/apps/online-store/app-proxies) take requests to Shopify links, and redirect them to external links. The `authenticate.public.appProxy` function validates requests made to app proxies, and returns a context to enable querying Shopify APIs. > Note: If the store has not installed the app, store-related properties such as `admin` or `storefront` will be `undefined` ### admin Use the `admin` object to interact with the admin GraphQL API.```typescript import { json } from "@remix-run/node"; 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 json({ data: productData.data }); } ``` ### liquid 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.```typescript import {authenticate} from "~/shopify.server" export async function loader({ request }) { const {liquid} = await authenticate.public.appProxy(request); return liquid("Hello {{shop.name}}"); } ``` Set the `layout` option to `false` to render the Liquid content without a theme.```typescript import {authenticate} from "~/shopify.server" export async function loader({ request }) { const {liquid} = await authenticate.public.appProxy(request); return liquid( "Hello {{shop.name}}", { layout: false } ); } ``` Handle form submissions through an app proxy.```typescript import { redirect } from "@remix-run/node"; import { authenticate } from "~/shopify.server"; export async function loader({ request }) { const { liquid } = await authenticate.public.appProxy(request); return liquid(`
`); } 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"); } ``` ### session Get the session for the shop that initiated the request to the app proxy.```typescript import { json } from "@remix-run/node"; 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 json( await getMyAppModelData({shop: session.shop}) ); }; ``` ### storefront Use the `storefront` object to interact with the GraphQL API.```typescript import { json } from "@remix-run/node"; 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 json(await response.json()); } ``` ## Examples [App proxies](https://shopify.dev/docs/apps/online-store/app-proxies) take requests to Shopify links, and redirect them to external links. The `authenticate.public.appProxy` function validates requests made to app proxies, and returns a context to enable querying Shopify APIs. > Note: If the store has not installed the app, store-related properties such as `admin` or `storefront` will be `undefined` ### admin Use the `admin` object to interact with the admin GraphQL API.```typescript import { json } from "@remix-run/node"; 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 json({ data: productData.data }); } ``` ### liquid 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.```typescript import {authenticate} from "~/shopify.server" export async function loader({ request }) { const {liquid} = await authenticate.public.appProxy(request); return liquid("Hello {{shop.name}}"); } ``` Set the `layout` option to `false` to render the Liquid content without a theme.```typescript import {authenticate} from "~/shopify.server" export async function loader({ request }) { const {liquid} = await authenticate.public.appProxy(request); return liquid( "Hello {{shop.name}}", { layout: false } ); } ``` Handle form submissions through an app proxy.```typescript import { redirect } from "@remix-run/node"; import { authenticate } from "~/shopify.server"; export async function loader({ request }) { const { liquid } = await authenticate.public.appProxy(request); return liquid(`
`); } 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"); } ``` ### session Get the session for the shop that initiated the request to the app proxy.```typescript import { json } from "@remix-run/node"; 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 json( await getMyAppModelData({shop: session.shop}) ); }; ``` ### storefront Use the `storefront` object to interact with the GraphQL API.```typescript import { json } from "@remix-run/node"; 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 json(await response.json()); } ```