Skip to main content

Admin
object

Contains functions for authenticating and interacting with the Admin API.

This function can handle requests for apps embedded in the Admin, Admin extensions, or non-embedded apps.

Authenticates requests coming from the Shopify admin.

The shape of the returned object changes depending on the isEmbeddedApp config.

Request
required

Promise<<Config, Resources>>
Was this section helpful?

Authenticate, run API mutation, and redirect

/app/routes/**.ts

import {type ActionFunctionArgs, json} from '@remix-run/node';
import {GraphqlQueryError} from '@shopify/shopify-api';

import {authenticate} from '../shopify.server';

export const action = async ({request}: ActionFunctionArgs) => {
const {admin, redirect} = await authenticate.admin(request);

try {
await admin.graphql(
`#graphql
mutation updateProductTitle($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
}
}
}`,
{
variables: {
input: {id: '123', title: 'New title'},
},
},
);

return redirect('/app/product-updated');
} catch (error) {
if (error instanceof GraphqlQueryError) {
return json({errors: error.body?.errors}, {status: 500});
}

return new Response('Failed to update product title', {status: 500});
}
};

Get user-specific data using the sessionToken object.

Was this section helpful?

Using the decoded session token

import { LoaderFunctionArgs, json } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { getMyAppData } from "~/db/model.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { sessionToken } = await authenticate.admin(
request
);
return json(await getMyAppData({user: sessionToken.sub}));
};

Use the redirect helper to safely redirect between pages.

Anchor to example-redirecting-outside-of-shopify-adminRedirecting outside of Shopify admin

Pass in a target option of _top or _parent to go to an external URL.

Was this section helpful?

Redirecting to an app route

/app/routes/admin/my-route.ts

import { LoaderFunctionArgs, json } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { session, redirect } = await authenticate.admin(request);
return redirect("/");
};

Get your app's shop-specific data using an offline session.

Get your app's user-specific data using an online session.

Was this section helpful?

Using offline sessions

import { LoaderFunctionArgs, json } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { getMyAppData } from "~/db/model.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { session } = await authenticate.admin(request);
return json(await getMyAppData({shop: session.shop));
};

Anchor to example-setting-cors-headers-for-a-admin-requestSetting CORS headers for a admin request

Use the cors helper to ensure your app can respond to requests from admin extensions.

Was this section helpful?

Setting CORS headers for a admin request

/app/routes/admin/my-route.ts

import { LoaderFunctionArgs, json } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { getMyAppData } from "~/db/model.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { session, cors } = await authenticate.admin(request);
return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));
};

Getting the number of orders in a store using REST resources. Visit the Admin REST API references for examples on using each resource.

Anchor to example-performing-a-get-request-to-the-rest-apiPerforming a GET request to the REST API

Use admin.rest.get to make custom requests to make a request to to the customer/count endpoint

Anchor to example-performing-a-post-request-to-the-rest-apiPerforming a POST request to the REST API

Use admin.rest.post to make custom requests to make a request to to the customers.json endpoint to send a welcome email

Was this section helpful?

Using REST resources

import { LoaderFunctionArgs, json } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const {
admin,
session,
} = await authenticate.admin(request);

return json(
admin.rest.resources.Order.count({ session }),
);
};

Use admin.graphql to make query / mutation requests.

Catch GraphqlQueryError errors to see error messages from the API.

Was this section helpful?

Querying the GraphQL API

import { ActionFunctionArgs } from "@remix-run/node";
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 json({
productId: productData.data?.productCreate?.product?.id,
});
}

Call billing.request in the onFailure callback to immediately redirect to the Shopify page to request payment.

When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.

Call billing.request with the v3_lineItemBilling future flag enabled

Was this section helpful?

Requesting billing right away

import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticate, MONTHLY_PLAN } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { billing } = await authenticate.admin(request);
await billing.require({
plans: [MONTHLY_PLAN],
isTest: true,
onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),
});

// App logic
};

Anchor to example-check-what-billing-plans-a-merchant-is-subscribed-toCheck what billing plans a merchant is subscribed to

Use billing.check if you want to determine which plans are in use. Unlike require, check does notthrow an error if no active billing plans are present.

Was this section helpful?

Check what billing plans a merchant is subscribed to

import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticate, MONTHLY_PLAN } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { billing } = await authenticate.admin(request);
const { hasActivePayment, appSubscriptions } = await billing.check({
plans: [MONTHLY_PLAN],
isTest: false,
});
console.log(hasActivePayment)
console.log(appSubscriptions)
};

Change where the merchant is returned to after approving the purchase using the returnUrl option.

Was this section helpful?

Using a custom return URL

import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticate, MONTHLY_PLAN } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { billing } = await authenticate.admin(request);
await billing.require({
plans: [MONTHLY_PLAN],
onFailure: async () => billing.request({
plan: MONTHLY_PLAN,
isTest: true,
returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',
}),
});

// App logic
};

Use the billing.cancel function to cancel an active subscription with the id returned from billing.require.

Was this section helpful?

Cancelling a subscription

import { LoaderFunctionArgs } from "@remix-run/node";
import { authenticate, MONTHLY_PLAN } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
const { billing } = await authenticate.admin(request);
const billingCheck = await billing.require({
plans: [MONTHLY_PLAN],
onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),
});

const subscription = billingCheck.appSubscriptions[0];
const cancelledSubscription = await billing.cancel({
subscriptionId: subscription.id,
isTest: true,
prorate: true,
});

// App logic
};