This package makes it easy to use Remix to build Shopify apps.
It builds on the @shopify/shopify-api
package and exposes a shopifyApp
function. You can use shopifyApp
to configure your app and then authenticate requests from Shopify.
Visit the shopify.dev
documentation for more details on the Remix app package.
To use this package, you will need to have:
- a Shopify Partner account and development store
- an app already set up on your partner account
- a JavaScript package manager such as yarn installed
The easiest way to get started with developing Shopify apps is using the Shopify CLI. It helps you set up your environment for developing and publishing your apps and extensions.
We strongly recommend using the CLI to create and manage your Remix apps and extensions! Refer to the getting started documentation to create your app using the Shopify CLI.
This package works with any Remix app. If you're starting an app from scratch, then you can create a brand new Remix app that uses the indie-stack:
npx create-remix@latest --template remix-run/indie-stack
cd ./name-of-your-app
Now let's install this package:
npm install @shopify/shopify-app-remix
Next, you'll need to set up some routes and headers so you can embed your app in the Shopify admin. You can find more details on all the steps described here in the package documentation.
Create app/shopify.server.js
. We will use this file to configure our Shopify app by calling the shopifyApp
function:
// app/shopify.server.js
// Note that you don't need to import the node adapter if you're running on a different runtime.
import '@shopify/shopify-app-remix/server/adapters/node';
// Memory storage makes it easy to set an app up, but should never be used in production.
import {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory';
import {LATEST_API_VERSION, shopifyApp} from '@shopify/shopify-app-remix';
const shopify = shopifyApp({
apiKey: process.env.SHOPIFY_API_KEY!,
apiSecretKey: process.env.SHOPIFY_API_SECRET!,
appUrl: process.env.SHOPIFY_APP_URL!,
scopes: ['read_products'],
apiVersion: LATEST_API_VERSION,
sessionStorage: new MemorySessionStorage(),
});
export default shopify;
This will require some environment variables. So let's create an .env
file:
SHOPIFY_API_KEY="[Copy from partners dashboard]"
SHOPIFY_API_SECRET="[Copy from partners dashboard]"
SHOPIFY_APP_URL="[The tunnel URL you are using to run your app]"
shopifyApp
needs to reserve a splat route for auth.
It should export a loader that uses shopifyApp
to authenticate:
// app/routes/auth/$.tsx
import {LoaderFunctionArgs} from '@remix-run/node';
import shopify from '~/shopify.server';
export async function loader({request}: LoaderFunctionArgs) {
await shopify.authenticate.admin(request);
return null;
}
Next, set up the AppProvider
component in your app's routes. To do this pass the process.env.SHOPIFY_API_KEY
to the frontend via the loader.
Here is an example:
// root.tsx
import {LoaderFunctionArgs} from '@remix-run/node';
import {AppProvider} from '@shopify/shopify-app-remix/react';
import shopify from '~/shopify.server';
export async function loader({request}: LoaderFunctionArgs) {
await shopify.authenticate.admin(request);
return json({
apiKey: process.env.SHOPIFY_API_KEY,
});
}
export default function App() {
const {apiKey} = useLoaderData<typeof loader>();
return (
<html>
<head>
<Meta />
<Links />
</head>
<body>
<AppProvider apiKey={apiKey} isEmbeddedApp>
<Outlet />
</AppProvider>
</body>
</html>
);
}
This component will set up Polaris and App Bridge. If your app isn't embedded, set the isEmbeddedApp
prop to false
.
Now that your app is ready to respond to requests, it will also need to add the required Content-Security-Policy
header directives, as per our documentation.
To do that, this package provides the shopify.addDocumentResponseHeaders
method.
You should return these headers from any endpoint that renders HTML in your app. Most likely you'll want to add this to every HTML response by updating the entry.server.tsx file:
// entry.server.tsx
import shopify from './shopify.server';
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
shopify.addDocumentResponseHeaders(request, responseHeaders);
/// ..etc
}
To run your app and load it within the Shopify Admin, you need to:
- Update your app's URL in your Partners Dashboard app setup page to
http://localhost:8080
- Update your app's callback URL to
http://localhost:8080/api/auth/callback
in that same page - Go to Test your app in Partners Dashboard and select your development store
Once your app is up and running, you can start using this package to interact with Shopify APIs, webhooks and more.
Here are some guides to help you set up your app:
You can also authenticate requests from surfaces other than the admin.
To see all supported methods, see the shopify.authenticate
object documentation.
[!TIP] If you are building an embedded app, we strongly recommend using Shopify managed installation with token exchange instead of the legacy authorization code grant flow.
We've introduced a new installation and authorization strategy for embedded apps that eliminates the redirects that were previously necessary. It replaces the existing installation and authorization code grant flow.
This is achieved by using Shopify managed installation to handle automatic app installations and scope updates, while utilizing token exchange to retrieve an access token for authenticated API access.
[!NOTE] Newly created Remix apps from the template after February 1st 2024 has this feature enabled by default.
-
Enable Shopify managed installation by configuring your scopes through the Shopify CLI.
-
Enable the future flag
unstable_newEmbeddedAuthStrategy
in your app's server configuration file.// my-app/app/shopify.server.ts const shopify = shopifyApp({ ... isEmbeddedApp: true, future: { unstable_newEmbeddedAuthStrategy: true, } })
-
Enjoy a smoother and faster app installation process.
- How token exchange works
- Using Shopify managed installation
- Configuring access scopes through the Shopify CLI
This package exports a helper method through @shopify/shopify-app-remix/test-helpers
to simplify testing: testConfig()
. This method can be used to pass dummy configuration properties to shopifyApp()
.
If your testing framework supports setting environment variables, we recommend using an environment variable, for example "SHOPIFY_TESTING" to replace your default config with the config returned from testConfig()
.
// my-app/app/shopify.server.ts
import { testConfig } from "@shopify/shopify-app-remix/test-helpers";
...
const config = {
...
};
if (process.env.SHOPIFY_TESTING) {
Object.assign(config, testConfig());
}
const shopify = shopifyApp(config);
...
testConfig()
accepts a config object as an optional parameter. The config values provided override the default config values returned by testConfig()
. This is especially useful for integration testing and end-to-end testing to ensure shopifyApp()
reads the sessions from the development database.
// my-app/app/shopify.server.ts
import { testConfig } from "@shopify/shopify-app-remix/test-helpers";
...
const sessionStorage = new PrismaSessionStorage(prisma);
const config = {
...
sessionStorage,
...
};
if (process.env.SHOPIFY_TESTING) {
Object.assign(config, testConfig());
}
if (process.env.SHOPIFY_TESTING === "e2e") {
Object.assign(config, testConfig({ sessionStorage }));
}
...
If you're experiencing unexpected behaviors when using this package, check our app template's documentation for the solution to common issues.