Prisma is useful as an object-relational mapper (ORM) and Postgres is one of the supported database backend. Prisma is used to define the database schema in a simple Schema Definition Language (SDL) and can help with migrations and to auto generate the Prisma Client with type declarations for Javascript/Typescript. The generated Prisma Client is tailored to the models and views in the Schema and is used to interact with the database.
provider
and url
in prisma/schema.prisma
file as follows:datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_DATABASE_URL")
}
.env
file with the following variables:DATABASE_URL="postgres://<user>:<password>@<host>:<port>/<database>?pgbouncer=true"
DIRECT_DATABASE_URL="postgresql://<user>:<password>@<host>:<port>/<database>"
Notes:
<user>
, <password>
, <host>
, <port>
, and <database>
placeholders with the actual values.DATABASE_URL
is prefixed with postgres://
and the DIRECT_DATABASE_URL
is prefixed with postgresql://
.prisma/schema.prisma
by convention.npx prisma migrate dev --name init
to create the database schema and generate Prisma Client.schema.prisma
to include postgresqlExtensions
previewFeatures
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
npx prisma db pull
pgvector
extension to the datasource
block in schema.prisma
:datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_DATABASE_URL")
extensions = [pgvector(map: "vector", schema: "extensions")]
}
NOTE: The
map
andschema
are optional arguments for the extension. You could just use:extensions = [vector]
. Theextensions = [pgvector(map: "vector", schema: "extensions")]
is useful when using Supabase.
npx prisma db push
to update the database schema and generate Prisma Client. This will generate Prisma Client but not record the schema change history in the database.npx prisma migrate dev --name <migration-name>
to create a new migration and apply it to the database.npx prisma migrate dev --name <migration-name>
to create a new migration and apply it to the database.npx prisma generate
to generate Prisma Client.npx prisma studio
to open the Prisma Studio GUI to view the data in the database.For a complete reference sample, let’s define the data model in schema.prisma
as follows:
generator client {
provider = "prisma-client-js"
previewFeatures = ["postgresqlExtensions"]
}
datasource db {
provider = "postgresql"
url = "DATABASE_URL"
extensions = [vector]
}
model Embedding{
id Int @id @default(autoincrement())
text String
vector Unsupported("vector(4)") // vector(n) for a n-dimensional vector.
}
Note: We have to use
Unsupported("vector(4)")
for thevector
field because Prisma Schema Language (Schema) does not support thevector
type (as of Prisma 4.13.0).
With the above Data model, we can use Prisma to store and retrieve vector embeddings in PostgreSQL with PgVector extension. The vector
field is of type Unsupported("vector(4)")
which is a 4-dimensional vector. You can update this to suit your actual embedding size for example 1536
when using OpenAI’s text-embedding-*-002
models.The vector
field is stored in the vector
column in the Embedding
table in the database.
Apply the above schema to the database depending on your situation:
npx prisma migrate dev --name init
to create the database schema and generate Prisma Client.npx prisma introspect
to generate the Prisma schema from the database schema and then use npx prisma generate
to generate Prisma Client. OR you can use baselining to initialize the database schema and generate Prisma Client. See Baselining for more details.The generated SQL migration script under prisma/migrations/
will look like this:
-- CreateExtension
CREATE EXTENSION IF NOT EXISTS "vector" WITH SCHEMA "extensions";
-- CreateTable
CREATE TABLE "Embedding" (
"id" SERIAL NOT NULL,
"text" TEXT NOT NULL,
"vector" vector(4) NOT NULL,
CONSTRAINT "Embedding_pkey" PRIMARY KEY ("id")
);
After you have applied the above schema to the database (using prisma migrate
or prisma db push
), the database will have the vector
field type created in the corresponding (Embedding
) table as per the SQL above.
Since the Prisma Schema language does not support the vector
type, the generated Prisma Client will not include the vector
field in the Embedding
model. For example, the following Application code to insert vector data into a PostgreSQL database using Prisma does not work:
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
const embedding = await prisma.embedding.create({
data: {
text: "Hello World",
// vector: [1, 2, 3, 4], // This does not work.
},
});
console.log(embedding);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
See Prisma docs on Unsupported field types for more information. You can still execute raw SQL to insert vector data into your database via Prisma. Let’s see how in the next section below.
The following sample script uses prisma.$executeRaw
to insert vector embeddings into a postgreSQL database with the pgvector
extension installed and updated using the Prisma data model and migration/SQL script discussed above.
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
// Generate embeddings
const vectorEmbedding1 = JSON.stringify([1, 2, 3, 4]);
const vectorEmbedding2 = JSON.stringify([64, 256, 512, 1024]);
// Insert embeddings into DB
await prisma.$executeRaw`INSERT INTO embedding (vector) VALUES (${vectorEmbedding1}::vector), (${vectorEmbedding2}::vector)`;
// Search/Query and retrieve embeddings
const results =
await prisma.$queryRaw`SELECT id, embedding::text FROM embedding ORDER BY vector >-> ${vecEmbed}::vector LIMIT 2`;
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
The following simple model is not feasible with Prisma:
model File {
id Int @id @default(autoincrement())
name String
chunks Embedding[] // Composite type. Not supported by Prisma.
}
model Embedding{
id Int @id @default(autoincrement())
embedding Unsupported("vector")
}
The generated Prisma Client will not include the chunks
field in the File
model.
The generated migrations.sql
looks like this:
-- CreateExtension
CREATE EXTENSION IF NOT EXISTS "vector" WITH SCHEMA "extensions";
-- CreateTable
CREATE TABLE "File" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
CONSTRAINT "File_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Embedding" (
"id" SERIAL NOT NULL,
"text" TEXT NOT NULL,
"embedding" vector,
CONSTRAINT "TextEmbedding_pkey" PRIMARY KEY ("id")
);
Related issues/feature-requests Reuse collections of fields inside models and, Support for native DB composite types has been open since 2020!
There is a helpful troubleshooting section for some of the common issues when using Prisma with Supabase here.