Architecture
This page covers how core Prisma Client Python features work internally. It's intended to aid in inderstanding the code and hopefully enabling more people to contribute.
Note
This page assumes you have at least a basic working knowledge of python.
Design Principles
-
Type Safety
Every python API operation must have accurate type definitions.
Overview
Prisma Client Python is made up of two core components, Client generation and the Client API.
Generation
Parsing schema files and invoking generators is handled internally by Prisma.
Behind the scenes, the provider value is simply a pointer to a shell script, for example, the following generators are all functionally equivalent.
generator client {
provider = "prisma-client-py"
}
generator client {
provider = "python3 -m prisma"
}
generator client {
provider = "python3 -c 'from prisma.cli import main; main()'"
}
How Does it Work?
Prisma sends data to generators using JSON-RPC, our implementation can be found at src/prisma/generator/jsonrpc.py
and the communication with Prisma is implemented in the BaseGenerator.invoke()
function in src/prisma/generator/generator.py
Control flow:
prisma generate
- Prisma starts generator process
- Generator process waits for message from prisma
- Prisma sends
getManifest
for generator metadata - Generator responds and waits for a new message
- Prisma sends
generate
- Generator writes the python files to disk and sends empty response
- Prisma closes the generator process
When the generate
message is sent, prisma includes a DMMF which is the Prisma Schema AST which represents the Prisma Schema File in JSON form.
We then parse the DMMF into pydantic models, this ensures our code is expecting the same DMMF structure that prisma is providing. The DMMF models can be found in src/prisma/generator/models.py
.
We then use Jinja2 to render the python files and then write them to the output location.
Client API
In short, the Client API is a wrapper over the GraphQL API exposed by the Prisma Query Engine.
How Does it Work?
Connecting
When Client.connect()
is called the following steps are taken:
- Find the query engine binary
- Start the query engine process
- Send health checks to the HTTP API until the server is ready
Executing queries
Queries are executed by sending a GraphQL request to the query engine server, queries are built by the rendered builder.py
file which corresponds to the src/prisma/generator/templates/builder.py.jinja
template.
Queries look something like this:
query {
result: findUniqueUser
(
where: {
id: "ckq23ky3003510r8zll5m2hma"
}
)
{
id
name
profile {
id
user_id
bio
}
}
}
Database operations are handled by Prisma's query engine binary, see this graph from the prisma documentation.