# 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 ++] // [!code ++] {" "} // [!code ++] {" "} // [!code ++] {" "} // [!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