---
title: App proxy
description: >-
[App proxies](/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`
api_version: v3
api_name: shopify-app-remix
source_url:
html: >-
https://shopify.dev/docs/api/shopify-app-remix/v3/authenticate/public/app-proxy
md: >-
https://shopify.dev/docs/api/shopify-app-remix/v3/authenticate/public/app-proxy.md
---
# 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\
## authenticate.public.appProxy(**[request](#authenticatepublicappproxy-propertydetail-request)**)
Authenticates requests coming to the app from Shopify app proxies.
### Parameters
* **request**
**Request**
**required**
### Returns
* **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.
```ts
undefined
```
* liquid
A utility for creating a Liquid Response.
```ts
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.
```ts
undefined
```
* storefront
No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available.
```ts
undefined
```
```ts
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
```ts
string
```
* initAndOptions
```ts
number | (ResponseInit & Options)
```
Response
```ts
Response
```
```ts
export type LiquidResponseFunction = (
body: string,
initAndOptions?: number | (ResponseInit & Options),
) => Response;
```
### Options
* layout
Whether to use the shop's theme layout around the Liquid content.
```ts
boolean
```
```ts
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.
```ts
AdminApiContext
```
* liquid
A utility for creating a Liquid Response.
```ts
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.
```ts
Session
```
* storefront
Method for interacting with the Shopify Storefront Graphql API for the store that made the request.
```ts
StorefrontContext
```
````ts
export interface AppProxyContextWithSession<
ConfigArg extends AppConfigArg,
Resources extends ShopifyRestResources = ShopifyRestResources,
> 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
* Using the session object.
* Get the session for the shop that initiated the request to the app proxy.
* ```ts
* // app/routes/**\/.ts
* 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})
* );
* };
* ```
*/
session: Session;
/**
* Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.
*
* @example
* Interacting with the Admin API.
* Use the `admin` object to interact with the admin GraphQL API.
* ```ts
* // app/routes/**\/.ts
* 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 });
* }
* ```
*/
admin: AdminApiContext;
/**
* Method for interacting with the Shopify Storefront Graphql API for the store that made the request.
*
* @example
* Interacting with the Storefront API.
* Use the `storefront` object to interact with the GraphQL API.
* ```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(
* `#graphql
* query blogIds {
* blogs(first: 10) {
* edges {
* node {
* id
* }
* }
* }
* }`
* );
*
* return json(await response.json());
* }
* ```
*/
storefront: StorefrontContext;
}
````
### 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-remix/tree/main#incorrect-graphql-hints).
```ts
GraphQLClient
```
````ts
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;
}
````
### GraphQLClient
* query
```ts
Operation extends keyof Operations
```
* options
```ts
GraphQLQueryOptions
```
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\; }
```ts
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;
}
```
```ts
export type GraphQLClient = <
Operation extends keyof Operations,
>(
query: Operation,
options?: GraphQLQueryOptions,
) => Promise>;
```
### GraphQLQueryOptions
* apiVersion
The version of the API to use for the request.
```ts
ApiVersion
```
* headers
Additional headers to include in the request.
```ts
Record
```
* signal
An optional AbortSignal to cancel the request.
```ts
AbortSignal
```
* tries
The total number of times to try the request if it fails.
```ts
number
```
* variables
The variables to pass to the operation.
```ts
ApiClientRequestOptions["variables"]
```
```ts
export interface GraphQLQueryOptions<
Operation extends keyof Operations,
Operations extends AllOperations,
> {
/**
* The variables to pass to the operation.
*/
variables?: ApiClientRequestOptions['variables'];
/**
* The version of the API to use for the request.
*/
apiVersion?: ApiVersion;
/**
* Additional headers to include in the request.
*/
headers?: Record;
/**
* The total number of times to try the request if it fails.
*/
tries?: number;
/**
* An optional AbortSignal to cancel the request.
*/
signal?: AbortSignal;
}
```
Examples
### Examples
* #### Authenticate and fetch product information
##### Description
Authenticate and fetch product information
##### /app/routes/\*\*.ts
```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}}`);
};
```
* #### Interacting with the Admin API
##### Description
Use the \`admin\` object to interact with the admin GraphQL API.
##### app/routes/\*\*\\/.ts
```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 });
}
```
* #### 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
```typescript
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
```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 }
);
}
```
* #### Rendering a form in a Liquid response
##### Description
Handle form submissions through an app proxy.
##### app/routes/apps.proxy.my-action.tsx
```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");
}
```
* #### Using the session object
##### Description
Get the session for the shop that initiated the request to the app proxy.
##### app/routes/\*\*\\/.ts
```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})
);
};
```
* #### Interacting with the Storefront API
##### Description
Use the \`storefront\` object to interact with the GraphQL API.
##### app/routes/\*\*\\/.ts
```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());
}
```
## Related
[Interact with the Admin API. - Admin API context](https://shopify.dev/docs/api/shopify-app-remix/apis/admin-api)
[Interact with the Storefront API. - Storefront API context](https://shopify.dev/docs/api/shopify-app-remix/apis/storefront-api)
[Use the shop's theme to render a template. - Liquid reference](https://shopify.dev/docs/api/liquid)