# Shopify (/docs/guides/integrations/shopify)
> For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL.
Learn how to use Prisma Postgres with Shopify
Location: Guides > Integrations > Shopify
> [!NOTE]
> This guide uses Prisma ORM 7
>
> The commands and code on this page target Prisma ORM 7, which remains fully supported. To start a new Prisma ORM 8 project, see [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql); to add Prisma ORM 8 to an existing app, see [Add Prisma ORM to an existing PostgreSQL database](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql). A Prisma ORM 8 version is not available yet: `@shopify/shopify-app-session-storage-prisma` requires Prisma Client, which Prisma ORM 8 replaces with the `@prisma/orm-postgres` runtime.
## Introduction [#introduction]
[Shopify](https://www.shopify.com/) is a popular platform for building e-commerce stores. This guide will show you how to connect a Shopify app to a [Prisma Postgres](https://www.prisma.io/postgres) database in order to create internal notes for products.
## Prerequisites [#prerequisites]
* [Node.js](https://nodejs.org/en/download/)
* [Shopify CLI](https://shopify.dev/docs/api/shopify-cli)
* [Shopify Partner Account](https://www.shopify.com/partners) and a [development store](https://shopify.dev/docs/api/development-stores#create-a-development-store-to-test-your-app)
## 1. Set up your project [#1-set-up-your-project]
> [!NOTE]
> If you do not have the Shopify CLI installed, you can install it with `npm install -g @shopify/cli`.
To start, initialize a new Shopify app using the Shopify CLI:
```bash
shopify app init
```
During setup, you'll be prompted to customize your app. Choose the following options:
> [!NOTE]
> * *Get started building your app:* `Build a Remix app (recommended)`
> * *For your Remix template, which language do you want:* `JavaScript`
> * *App Name:* `prisma-store` *(name cannot contain `shopify`)*
Navigate to the `prisma-store` directory:
```bash
cd prisma-store
```
## 2. Set up Prisma [#2-set-up-prisma]
Prisma comes pre-installed in your project. In this section, you update it to Prisma ORM 7 and switch the app from SQLite to Prisma Postgres.
You will be swapping to a Prisma Postgres database, so delete the `migrations` folder along with the `dev.sqlite` file, inside of the `prisma` directory.
You need to update a few things in the `schema.prisma` file to get it working with Remix and Prisma Postgres.
* Swap to the new `prisma-client` generator.
* Update the provider to `postgresql`.
* Update the url to the new database URL.
```prisma title="prisma/schema.prisma"
generator client {
provider = "prisma-client-js" // [!code --]
provider = "prisma-client" // [!code ++]
output = "../app/generated/prisma" // [!code ++]
}
datasource db {
provider = "sqlite" // [!code --]
provider = "postgresql" // [!code ++]
url = "file:../dev.db" // [!code --]
}
model Session {
// ... existing model
}
```
Create a `prisma.config.ts` file to configure Prisma:
```typescript title="prisma.config.ts"
import "dotenv/config"; // [!code ++]
import { defineConfig, env } from "prisma/config"; // [!code ++]
// [!code ++]
export default defineConfig({
// [!code ++]
schema: "prisma/schema.prisma", // [!code ++]
migrations: {
// [!code ++]
path: "prisma/migrations", // [!code ++]
}, // [!code ++]
datasource: {
// [!code ++]
url: env("DATABASE_URL"), // [!code ++]
}, // [!code ++]
}); // [!code ++]
```
> [!NOTE]
> Since Shopify apps typically have dotenv pre-installed, you should already have access to it. If not, install it with:
>
>
>
>
> #### bun
> ```bash
> bun add dotenv
> ```
>
>
> #### pnpm
> ```bash
> pnpm add dotenv
> ```
>
>
> #### yarn
> ```bash
> yarn add dotenv
> ```
>
>
> #### npm
> ```bash
> npm install dotenv
> ```
>
>
To enable your app to store notes for each product, add a new `ProductNote` model to your Prisma schema.
This model will allow you to save and organize notes linked to individual products in your database through the `productGid` field.
```prisma title="prisma/schema.prisma"
generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
datasource db {
provider = "postgresql"
}
model Session {
// ... existing model
}
model ProductNote { // [!code ++]
id String @id @default(uuid()) // [!code ++]
productGid String // [!code ++]
body String? // [!code ++]
createdAt DateTime @default(now()) // [!code ++]
updatedAt DateTime @updatedAt // [!code ++]
} // [!code ++]
```
Next, Prisma will need to be updated to the latest version. Run:
#### bun
```bash
bun add prisma@prev @types/pg --dev
```
#### pnpm
```bash
pnpm add prisma@prev @types/pg --save-dev
```
#### yarn
```bash
yarn add prisma@prev @types/pg --dev
```
#### npm
```bash
npm install prisma@prev @types/pg --save-dev
```
#### bun
```bash
bun add @prisma/client@7 @prisma/adapter-pg pg
```
#### pnpm
```bash
pnpm add @prisma/client@7 @prisma/adapter-pg pg
```
#### yarn
```bash
yarn add @prisma/client@7 @prisma/adapter-pg pg
```
#### npm
```bash
npm install @prisma/client@7 @prisma/adapter-pg pg
```
> [!NOTE]
> If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of `@prisma/adapter-pg`. For more information, see [Database drivers](https://www.prisma.io/docs/orm/v7/core-concepts/supported-databases/database-drivers).
Initialize Prisma in your project:
#### bun
```bash
bunx --bun prisma init
```
#### pnpm
```bash
pnpm prisma init
```
#### yarn
```bash
yarn prisma init
```
#### npm
```bash
npx prisma init
```
Then create a Prisma Postgres database:
#### bun
```bash
bunx create-db
```
#### pnpm
```bash
pnpm dlx create-db
```
#### yarn
```bash
yarn dlx create-db
```
#### npm
```bash
npx create-db
```
Copy the connection string from the CLI output. It should look similar to this:
```text
DATABASE_URL="postgresql://user:password@host:5432/database?sslmode=require"
```
Replace the generated `DATABASE_URL` in your `.env` file with the value from `npx create-db`.
Apply your database schema:
#### bun
```bash
bunx prisma migrate dev --name init
```
#### pnpm
```bash
pnpm prisma migrate dev --name init
```
#### yarn
```bash
yarn prisma migrate dev --name init
```
#### npm
```bash
npx prisma migrate dev --name init
```
Then generate Prisma Client:
#### bun
```bash
bunx prisma generate
```
#### pnpm
```bash
pnpm prisma generate
```
#### yarn
```bash
yarn prisma generate
```
#### npm
```bash
npx prisma generate
```
Next, update your `db.server.ts` file to use the newly generated Prisma client with the driver adapter:
```tsx title="app/db.server.ts"
import { PrismaClient } from "@prisma/client"; // [!code --]
import { PrismaClient } from "./generated/prisma/client.js"; // [!code ++]
import { PrismaPg } from "@prisma/adapter-pg"; // [!code ++]
// [!code ++]
const adapter = new PrismaPg({
// [!code ++]
connectionString: process.env.DATABASE_URL, // [!code ++]
}); // [!code ++]
if (process.env.NODE_ENV !== "production") {
if (!global.prismaGlobal) {
global.prismaGlobal = new PrismaClient(); // [!code --]
global.prismaGlobal = new PrismaClient({ adapter }); // [!code ++]
}
}
const prisma = global.prismaGlobal ?? new PrismaClient(); // [!code --]
const prisma = global.prismaGlobal ?? new PrismaClient({ adapter }); // [!code ++]
export default prisma;
```
> [!WARNING]
> It is recommended to add `app/generated/prisma` to your `.gitignore` file.
## 3. Create your Remix model [#3-create-your-remix-model]
Create a new `models/` folder. Inside this folder, add a file named `notes.server.js` to hold the note-related database logic.
The `notes.server.js` file will contain two functions:
* `getNotes` - This will get all the notes for a given product.
* `createNote` - This will create a new note for a given product.
Start by importing the Prisma client from `db.server.ts` and creating the `getNotes` function:
```js title="models/notes.server.js"
import prisma from "../db.server"; // [!code ++]
// [!code ++]
export const getNotes = async (productGid) => {
// [!code ++]
const notes = await prisma.productNote.findMany({
// [!code ++]
where: { productGid: productGid.toString() }, // [!code ++]
orderBy: { createdAt: "desc" }, // [!code ++]
}); // [!code ++]
return notes; // [!code ++]
}; // [!code ++]
```
To enable users to add new notes to your database, create a function in `notes.server.js` that uses `prisma.productNote.create`:
```js title="models/notes.server.js"
import prisma from "../db.server";
export const getNotes = async (productGid) => {
const notes = await prisma.productNote.findMany({
where: { productGid: productGid.toString() },
orderBy: { createdAt: "desc" },
});
return notes;
};
export const createNote = async (note) => {
// [!code ++]
const newNote = await prisma.productNote.create({
// [!code ++]
data: {
// [!code ++]
body: note.body, // [!code ++]
productGid: note.productGid, // [!code ++]
}, // [!code ++]
}); // [!code ++]
return newNote; // [!code ++]
}; // [!code ++]
```
## 4. Create your layout route [#4-create-your-layout-route]
Before those functions are able to be called, our route needs a layout to sit in. This layout route will feature a button for selecting a product, and will act as the parent for your `ProductNotes` route.
### 4.1. Create the ProductNotesLayout component [#41-create-the-productnoteslayout-component]
Start by creating the folder `routes/app.product-notes.jsx` and adding the `ProductNotesLayout` component inside of it:
```jsx title="app/routes/app.product-notes.jsx"
import { Page, Layout } from "@shopify/polaris"; // [!code ++]
// [!code ++]
export default function ProductNotesLayout() {
// [!code ++]
return (
// [!code ++]
{" "}
// [!code ++]
{" "}
// [!code ++]
// [!code ++]
{" "}
// [!code ++]
// [!code ++]
); // [!code ++]
} // [!code ++]
```
Next, create the `selectProduct` function and a `Button` to let the user pick a product:
```jsx title="app/routes/app.product-notes.jsx"
import { useNavigate } from "@remix-run/react";
import { Page, Layout } from "@shopify/polaris"; // [!code --]
import { Button, Page, Layout } from "@shopify/polaris"; // [!code ++]
export default function ProductNotesLayout() {
const navigate = useNavigate(); // [!code ++]
async function selectProduct() {
// [!code ++]
const products = await window.shopify.resourcePicker({
// [!code ++]
type: "product", // [!code ++]
action: "select", // [!code ++]
}); // [!code ++]
const selectedGid = products[0].id; // [!code ++]
navigate(`/app/product-notes/${encodeURIComponent(selectedGid)}`); // [!code ++]
} // [!code ++]
return (
{" "}
// [!code ++]
);
}
```
Remix renders provides the ability to render a nested route. Add an `` to the `routes/app.product-notes.jsx` file where the `ProductNotes` route will be rendered:
```jsx title="app/routes/app.product-notes.jsx"
import { useNavigate } from "@remix-run/react"; // [!code --]
import { Outlet, useNavigate } from "@remix-run/react"; // [!code ++]
import { Page, Button, Layout } from "@shopify/polaris";
export default function ProductNotesLayout() {
const navigate = useNavigate();
async function selectProduct() {
const products = await window.shopify.resourcePicker({
type: "product",
action: "select",
});
const selectedGid = products[0].id;
navigate(`/app/product-notes/${encodeURIComponent(selectedGid)}`);
}
return (
// [!code ++]
);
}
```
### 4.2. Add the ProductNotesLayout to the sidebar [#42-add-the-productnoteslayout-to-the-sidebar]
If you run `npm run dev`, you won't be able to see the `Product Notes` route. To fix this, you need to add the `ProductNotesLayout` to the `app.jsx` file so it shows up in the sidebar:
```jsx title="app/routes/app.jsx"
import { Link, Outlet, useLoaderData, useRouteError } from "@remix-run/react";
import { boundary } from "@shopify/shopify-app-remix/server";
import { AppProvider } from "@shopify/shopify-app-remix/react";
import { NavMenu } from "@shopify/app-bridge-react";
import polarisStyles from "@shopify/polaris/build/esm/styles.css?url";
import { authenticate } from "../shopify.server";
export const links = () => [{ rel: "stylesheet", href: polarisStyles }];
export const loader = async ({ request }) => {
await authenticate.admin(request);
return { apiKey: process.env.SHOPIFY_API_KEY || "" };
};
export default function App() {
const { apiKey } = useLoaderData();
return (
[ Home ](/app) [Product Notes](/app/product-notes) // [!code ++]
);
}
// Shopify needs Remix to catch some thrown responses, so that their headers are included in the response.
export function ErrorBoundary() {
return boundary.error(useRouteError());
}
export const headers = (headersArgs) => {
return boundary.headers(headersArgs);
};
```
## 5. Create your product notes route [#5-create-your-product-notes-route]
Currently, if you run `npm run dev` and navigate to the `Product Notes` route, you will see nothing once selecting a product.
Follow these steps to create the product notes route:
Create a new `routes/app/app.notes.$productGid.jsx` file which will take in the productGid as a parameter, and return the product notes associated with the product as well as a form to create a new note:
```jsx title="app/routes/app/app.notes.$productGid.jsx"
export default function ProductNotes() {
// [!code ++]
return (
// [!code ++]
<>> // [!code ++]
); // [!code ++]
} // [!code ++]
```
### 5.1. Render the notes [#51-render-the-notes]
On load, the route will need to fetch the notes for the product and display them.
Add a `loader` function to the route:
```jsx title="app/routes/app/app.notes.$productGid.jsx"
import { json } from "@remix-run/node"; // [!code ++]
import { useLoaderData } from "@remix-run/react"; // [!code ++]
import { getNotes } from "../models/note.server"; // [!code ++]
export const loader = async ({ params }) => {
// [!code ++]
const { productGid } = params; // [!code ++]
const notes = await getNotes(productGid); // [!code ++]
return json({ notes, productGid }); // [!code ++]
}; // [!code ++]
export default function ProductNotes() {
const { notes, productGid } = useLoaderData(); // [!code ++]
return <>>;
}
```
Map out the notes in the `ProductNotes` component, using Polaris components:
```jsx title="app/routes/app/app.notes.$productGid.jsx"
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { getNotes } from "../models/note.server";
import { Card, Layout, Text, BlockStack } from "@shopify/polaris"; // [!code ++]
export const loader = async ({ params }) => {
const { productGid } = params;
const notes = await getNotes(productGid);
return json({ notes, productGid });
};
export default function ProductNotes() {
const { notes, productGid } = useLoaderData();
return (
<>
{" "}
// [!code ++]
{" "}
// [!code ++]
{notes.length === 0 ? ( // [!code ++]
{" "}
// [!code ++] No notes yet. // [!code ++]
// [!code ++]
) : (
// [!code ++]
notes.map(
(
note, // [!code ++]
) => (
{" "}
// [!code ++]
{" "}
// [!code ++]
{note.body && ( // [!code ++]
{" "}
// [!code ++]
{note.body} // [!code ++]
// [!code ++]
)}{" "}
// [!code ++]
{" "}
// [!code ++] Added: {new Date(note.createdAt).toLocaleString()} // [!code ++]
{" "}
// [!code ++]
{" "}
// [!code ++]
// [!code ++]
),
) // [!code ++]
)}{" "}
// [!code ++]
{" "}
// [!code ++]
{" "}
// [!code ++]
>
);
}
```
You should be seeing "No notes yet.". If so, you're on the right track.
### 5.2. Add the form [#52-add-the-form]
A few things need to be added to the route in order to create a new note:
* Add an `action` function to the route.
* Display a `Toast` notification when a note is created.
* Import the `createNote` function from `models/note.server.js`.
* Import the `useActionData` and `useAppBridge`
```jsx title="app/routes/app/app.notes.$productGid.jsx"
import { json, redirect } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react"; // [!code --]
import { useLoaderData, useActionData } from "@remix-run/react"; // [!code ++]
import { getNotes } from "../models/note.server"; // [!code --]
import { getNotes, createNote } from "../models/note.server"; // [!code ++]
import { Card, Layout, Text, BlockStack } from "@shopify/polaris";
import { useAppBridge } from "@shopify/app-bridge-react"; // [!code ++]
export const loader = async ({ params }) => {
const { productGid } = params;
const notes = await getNotes(productGid);
return json({ notes, productGid });
};
export const action = async ({ request, params }) => {
// [!code ++]
const formData = await request.formData(); // [!code ++]
const body = formData.get("body")?.toString() || null; // [!code ++]
const { productGid } = params; // [!code ++]
// [!code ++]
await createNote({ productGid, body }); // [!code ++]
return redirect(`/app/product-notes/${encodeURIComponent(productGid)}`); // [!code ++]
}; // [!code ++]
export default function ProductNotes() {
const { notes, productGid } = useLoaderData();
const actionData = useActionData(); // [!code ++]
const app = useAppBridge(); // [!code ++]
useEffect(() => {
// [!code ++]
if (actionData?.ok) {
// [!code ++]
app.toast.show("Note saved", { duration: 3000 }); // [!code ++]
setBody(""); // [!code ++]
} // [!code ++]
}, [actionData, app]); // [!code ++]
return (
<>
{notes.length === 0 ? (
No notes yet.
) : (
notes.map((note) => (
{note.body && (
{note.body}
)}
Added: {new Date(note.createdAt).toLocaleString()}
))
)}
>
);
}
```
Now, you can build out the form that will call the `action` function:
```jsx title="app/routes/app/app.notes.$productGid.jsx"
import { json, redirect } from "@remix-run/node";
import { useLoaderData, useActionData } from "@remix-run/react";
import { getNotes, createNote } from "../models/note.server";
import { Card, Layout, Text, BlockStack } from "@shopify/polaris"; // [!code --]
import {
Card,
Layout,
Text,
BlockStack,
Form,
FormLayout,
TextField,
Button,
} from "@shopify/polaris"; // [!code ++]
import { useAppBridge } from "@shopify/app-bridge-react";
export const loader = async ({ params }) => {
const { productGid } = params;
const notes = await getNotes(productGid);
return json({ notes, productGid });
};
export const action = async ({ request, params }) => {
const formData = await request.formData();
const body = formData.get("body")?.toString() || null;
const { productGid } = params;
await createNote({ productGid, body });
return redirect(`/app/product-notes/${encodeURIComponent(productGid)}`);
};
export default function ProductNotes() {
const { notes, productGid } = useLoaderData();
const actionData = useActionData(); // [!code ++]
const app = useAppBridge(); // [!code ++]
useEffect(() => {
// [!code ++]
if (actionData?.ok) {
// [!code ++]
app.toast.show("Note saved", { duration: 3000 }); // [!code ++]
setBody(""); // [!code ++]
} // [!code ++]
}, [actionData, app]); // [!code ++]
return (
<>
{" "}
// [!code ++]
{" "}
// [!code ++]
{" "}
// [!code ++]
{" "}
// [!code ++]
{" "}
// [!code ++]
{notes.length === 0 ? (
No notes yet.
) : (
notes.map((note) => (
{note.body && (
{note.body}
)}
Added: {new Date(note.createdAt).toLocaleString()}
))
)}
>
);
}
```
You should now be able to add a note to a product and see it displayed.
### 6. Test your route [#6-test-your-route]
Run `npm run dev` and navigate to the `Product Notes` route.
* Navigate to Product Notes on the sidebar
* Select a product
* Add a note
* Verify that notes are displayed and saved correctly.
## Next Steps [#next-steps]
Now that you have a working Shopify app connected to a Prisma Postgres database, you can:
* Extend your Prisma schema with more models and relationships
* Add create/update/delete routes and forms
### More Info [#more-info]
* [Prisma Documentation](https://www.prisma.io/docs/orm/v7)
* [Shopify Dev Documentation](https://shopify.dev/docs)
## Related pages
- [`AI SDK (with Next.js)`](https://www.prisma.io/docs/guides/integrations/ai-sdk): Build a chat application with AI SDK, Prisma ORM, and Next.js that stores chat sessions and messages in Prisma Postgres.
- [`Datadog`](https://www.prisma.io/docs/guides/integrations/datadog): Learn how to configure Datadog tracing for a Prisma ORM project. Capture spans for every query using the @prisma/instrumentation package, dd-trace, and view them in Datadog
- [`Embedded Prisma Studio (with Next.js)`](https://www.prisma.io/docs/guides/integrations/embed-studio): Learn how to embed Prisma Studio directly in your Next.js application for database management
- [`GitHub Actions`](https://www.prisma.io/docs/guides/integrations/github-actions): Provision a Prisma Postgres database for every pull request with GitHub Actions and the Prisma CLI, apply your migrations and seed data to it, and delete it when the pull request closes.
- [`Permit.io`](https://www.prisma.io/docs/guides/integrations/permit-io): Learn how to implement access control with Prisma ORM with Permit.io