Simon Bundgaard-Egeberg
Posted on August 2, 2019
Prisma2 is a ORM/Database Migration tool which creates a GraphQL api from your server to your database.
This blog will cover some of the concepts of Prisma2, but it will not be a "getting started" tutorial, for this refer to Prisma's own documentation: Getting started
When creating a server with prisma2 and Photon we will need:
- To declare a database Schema in a prisma syntax.
- To create an app schema which will be exposed to the client.
Before we get into the graphql development, we need to understand what a GraphQL type and query is, because it is these we will implement on the server.
A graphql type
type User {
id: ID!
name: String
purchases: [Transaction!]
sells: [Transaction!]
username: String!
}
type Query {
user(userId: ID!): User
}
A query on the type
query MyNewUserQuery($id: ID!) {
user(userId: $id) {
name
id
username
}
}
Learn more about GraphQL here
Prisma Schema
The Prisma schema is much like writing in
SDL, except for some minor syntax changes, like the : between the field and fieldtype.
model User {
id String @default(cuid()) @id @unique
username String @unique
details UserDetails
purchases Transaction[] @relation(name: "TransactionToUser2")
sells Transaction[] @relation(name: "TransactionToUser")
}
model Transaction {
id String @default(cuid()) @id @unique
item Item
price Int
seller User @relation(name: "TransactionToUser")
buyer User @relation(name: "TransactionToUser2")
}
A few things to notice here, the id of each model is annotated and a id, which means it will be indexed in the database. Prisma gives a set of default functions which, in this case, can create a default UID cuid()
.
In the models above it is possible for a user to have a set of purchases and a set of sells. This means that there will be two different fields that reference the same model (the same goes for the User in the Transaction model), we can however name the relation, which makes sure the the correct fields map to the correct users.
Since Prisma2 is creating the database model, it is almost trivial to make a many to many relation by simply annotating each to have a array of the opposite type:
model User {
id String @default(cuid()) @id @unique
transactions Transaction[]
}
model Transaction {
id String @default(cuid()) @id @unique
users User[]
}
When the schema is written to the database, Prisma2 will generate a Photon API which can be instantiated and used to query the database.
If i was to return an array of all users i would be able to use photon like photon.users.findMany()
which would return a list of all users.
In the findMany function you can give a restriction object to restrict the returned data, if i only wanted the users which username was 'Yes'
(i don't know why though). ctx.photon.users.findMany({ where: { username: 'Yes' } })
,
the general type generated in the argument for the findMany is:
export declare type FindManyUserArgs = {
select?: UserSelect;
include?: UserInclude;
where?: UserWhereInput;
orderBy?: UserOrderByInput;
skip?: number;
after?: string;
before?: string;
first?: number;
last?: number;
};
select and include is used to blacklist/whitelist fields you want from the type.
Note that these types is generated to match the prisma schema, so all generated types might not match this type, but it paints a pretty picture of how well thought out this API is.
Pagination
As shown in above type you can do two different types of pagination, the "Take - Skip" and the "First - After" which is a cursor based pagination.
Prisma2 pushes the boundaries even further with @prisma/nexus which will from the generated types give you intellisense when creating your app schema. (This is the part where to select which data from the database you want to expose to the client)
// src/types/User.ts
import { objectType } from '@prisma/nexus';
export const User = objectType({
name: 'User',
definition(t) {
t.model.id();
t.field('computedField', {
type: 'String',
resolve: () => 'some computed value',
});
t.model.purchases({ type: 'Transaction' });
t.model.sells({ type: 'Transaction' });
t.model.username();
},
});
The generated types from Prisma2 will add the schematypes to the objectType global if the name property matches the model name.
So t.model.purchases({type: 'Transaction'})
is typesafely inferred from the User model shown in the prisma schema above.
However if i wanted a computed field, i can just add that with no fuss.
the above code will generate a graphql API you can query like shown in the start of the blogpost.
Talk about full circle =)
Posted on August 2, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.