# Unauthenticated storefront Allows interacting with the Storefront API when working outside of Shopify requests. This enables apps to integrate with 3rd party services and perform background tasks. > Caution: > This function doesn't perform **any** validation and shouldn't rely on raw user input. When using this function, consider the following: #### Background tasks Apps should ensure that the shop domain is authenticated when enqueueing jobs. #### 3rd party service requests Apps must obtain the shop domain from the 3rd party service in a secure way. ## unauthenticated.storefront Creates an unauthenticated Storefront context. ### GetUnauthenticatedStorefrontContext #### Returns: Promise #### Params: - shop: string export type GetUnauthenticatedStorefrontContext = ( shop: string, ) => Promise; ### UnauthenticatedStorefrontContext ### session The session for the given shop. This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice. This will always be an offline session. You can use this to get shop specific data. ### storefront Method for interacting with the Shopify GraphQL Storefront API for the given store. ### 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). ### 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 The version of the API to use for the request. ### headers Additional headers to include in the request. ### tries The total number of times to try the request if it fails. ### variables The variables to pass to the operation. ## Related - [API context](/docs/api/shopify-app-remix/apis/storefront-api) ## Examples Allows interacting with the Storefront API when working outside of Shopify requests. This enables apps to integrate with 3rd party services and perform background tasks. > Caution: > This function doesn't perform **any** validation and shouldn't rely on raw user input. When using this function, consider the following: #### Background tasks Apps should ensure that the shop domain is authenticated when enqueueing jobs. #### 3rd party service requests Apps must obtain the shop domain from the 3rd party service in a secure way. ### session ### Using the offline session ```typescript import { LoaderFunctionArgs, json } from "@remix-run/node"; import { unauthenticated } from "../shopify.server"; import { getMyAppData } from "~/db/model.server"; export const loader = async ({ request }: LoaderFunctionArgs) => { const shop = getShopFromExternalRequest(request); const { session } = await unauthenticated.storefront(shop); return json(await getMyAppData({shop: session.shop)); }; ``` ### storefront ### Querying the GraphQL API ```typescript import { json } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export async function action({ request }: ActionFunctionArgs) { const shop = getShopFromExternalRequest(request); const { storefront } = await unauthenticated.storefront(shop); const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`); return json(await response.json()); } ``` ### Handling GraphQL errors ```typescript import { ActionFunctionArgs } from "@remix-run/node"; import { authenticate } from "../shopify.server"; export const action = async ({ request }: ActionFunctionArgs) => { const shop = getShopFromExternalRequest(request); const { storefront } = await unauthenticated.storefront(shop); 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 }); } } ``` ```typescript import { shopifyApp } from "@shopify/shopify-app-remix/server"; const shopify = shopifyApp({ // ... }); export default shopify; export const unauthenticated = shopify.unauthenticated; ```