Introspection (PostgreSQL)
Overview
When connecting Prisma to an existing database that already has a database schema and/or contains some data, it can be tedious to manually write the datamodel from scratch while ensuring it matches the structure of the already existing data.
To automate this process, you can use the prisma introspect
command from the Prisma CLI to generate the datamodel based on the actual structure of the existing data.
The generated SDL serves as a foundation for your Prisma API, but you can easily make modifications afterwards as you see fit. Some common modifications include hiding a table from the Prisma API or renaming a column to a different name.
Introspecting a PostgreSQL database
PostgreSQL uses the following model to organize databases internally:
IMPORTANT: When introspecting a PostgreSQL database, you're actually introspecting a schema and not a database according to the illustrated model. Learn more here.
There are two ways you can use the CLI to introspect a PostgreSQL schema:
- Using the interactive
prisma init
wizard - Using the dedicated
prisma introspect
command
In both cases, you need to provide the connection details for the running PostgreSQL database. This includes the following:
- Host: The host of your PostgreSQL server, e.g.
localhost
. - Port: The port where your PostgreSQL server listens, e.g.
5432
. - User & Password: The credentials for your PostgreSQL server.
- Name of existing database: The name of the PostgreSQL database (according to the illustrated model from above).
- Use SSL (Yes/No): If your database connection is using SSL, you need to select
Yes
, otherwiseNo
. - Name of existing schema: The name of the PostgreSQL schema (according to the illustrated model from above), e.g.
public
.
Using the prisma init
wizard
During the interactive prisma init
flow you can choose to connect to an existing database with data. The CLI will ask for database connection details (as mentioned above) and verify that it can establish a successfully connection.
If the connection details are valid, the CLI will introspect the database and show you a summary.
When prisma init
terminates, the CLI has created the following files for you which you can now use to deploy a new Prisma service:
datamodel.prisma
: Contains the datamodel (in SDL) that was generated based on your existing database.docker-compose.yml
: The Docker Compose file containing the configuration of your Prisma server, including details about how to connect to your database- prisma.yml: The root configuration file for your service
To be able to query your PostgreSQL database using GraphQL you now need to deploy the service and open a GraphQL Playground:
prisma deploy
prisma playground
Using prisma introspect
prisma introspect
works in a similar way as the prisma init
wizard in that you need to provide the database connection information.
While prisma init
wizard generates an entire service configuration, prisma introspect
only generates the datamodel file:
datamodel-[TIMESTAMP].prisma
: The timestamp component allows you to use the introspect command for an existing Prisma service without overriding your existing datamodel.
Before deploying your service with the generated datamodel, you should ensure that the migrations
property (of the PRISMA_CONFIG
environment variable) in the Docker Compose file that specifies your Prisma server configuration is set to false
:
PRISMA_CONFIG: |
port: 4466
databases:
default:
connector: postgres
migrations: false # be sure this is set to false
host: localhost
port: 5432
user: postgres
password: postgres
database: postgres
schema: public
Setting migrations
to false
ensures that Prisma only updates the GraphQL API of the server based on the datamodel, but it does not perform migrations of your underlying PostgreSQL database.
Relations in the generated datamodel
Depending on your approach to model a relation between tables in your SQL database, the relation in the generated datamodel might look different. Here is the overview of how Prisma interprets relations specified in SQL:
Relation in SQL | Relation generated by Prisma (SDL) |
---|---|
Inline relation column | One-to-Many |
Relation table | Many-to-Many |
Relation table with extra column | Dedicated type to express relationship |
Inline relation column
A common way to represents relationships in a SQL database is via a foreign key constraint:
CREATE TABLE product (
id serial PRIMARY KEY UNIQUE
, description text NOT NULL
);
CREATE TABLE bill (
id serial PRIMARY KEY UNIQUE
, notes text NOT NULL
, product_id int REFERENCES product (id) ON UPDATE CASCADE
);
In this case, bill
uses a foreign key to reference the id
of the product
table and therefore create a relation between the two tables. Based on this table structure, Prisma generates two SDL models, Product
and Bill
, with a bidirectional relation (via the product
and bills
fields):
type Bill @pgTable(name: "bill") {
notes: String!
id: Int! @unique
product: Product @pgRelation(column: "product_id")
}
type Product @pgTable(name: "product") {
description: String!
id: Int! @unique
bills: [Bill!]!
}
Here is how Prisma generates the names for the generated fields:
type Bill
notes: String
is named after thenotes
collumn on thebill
tableid: Int!
is named after theid
column on thebill
tableproduct: Product
is named after theproduct_id
column on thebill
table; Prisma strips off the following suffixes of such foreign key field:_id
,ID
andId
type Product
description: String
is named after thedescription
collumn on theproduct
tableid: Int!
is named after theid
column on theproduct
tablebills: [Bill!]!
is named after the plural version of thebill
table
Relation tables
Another common approach to represent relations in a SQL database is via a dedicated relation table which "connects" the two related tables:
CREATE TABLE product (
id serial PRIMARY KEY UNIQUE
, product text NOT NULL
);
CREATE TABLE bill (
id serial PRIMARY KEY UNIQUE
, bill text NOT NULL
);
CREATE TABLE bill_product (
bill_id int REFERENCES bill (id) ON UPDATE CASCADE ON DELETE CASCADE
, product_id int REFERENCES product (id) ON UPDATE CASCADE
);
In this case, the relation table is called bill_prouct
and simply … simply contains two columns which both are foreign keys to the id
column of the bill
and product
tables.
Relation with extra columns
Sometimes it's helpful to enrich a relation table with meta-information about the relation itself:
CREATE TABLE product (
id serial PRIMARY KEY UNIQUE
, product text NOT NULL
);
CREATE TABLE bill (
id serial PRIMARY KEY UNIQUE
, bill text NOT NULL
);
CREATE TABLE bill_product (
bill_id int REFERENCES bill (id) ON UPDATE CASCADE ON DELETE CASCADE
, product_id int REFERENCES product (id) ON UPDATE CASCADE
, some_other_column text NOT NULL
);
If a relation table contains such extra information, Prisma treats the relation table as a dedicated type:
type Bill @pgTable(name: "bill") {
bill: String!
id: Int! @unique
bill_products: [Bill_product]
}
type Bill_product @pgTable(name: "bill_product") {
bill: Bill @pgRelation(column: "bill_id")
product: Product @pgRelation(column: "product_id")
some_other_column: String!
}
type Product @pgTable(name: "product") {
id: Int! @unique
product: String!
bill_products: [Bill_product]
}
This way you have the full flexibility to set and read the extra column(s) using normal queries and nested mutations.