First create generator and datasource entry in your .prisma file like below:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}model User {
id String @id @default(cuid()) // ID type column whose default value is cuid
createdAt DateTime @default(now()) // Date type column whose default value is current date and time
modifiedAt DateTime @default(now()) // Date type column whose default value is current date and time
email String @unique // String type column whose value must be unique
name String? // String type column whose value is optional
}model Question {
id String @id @default(cuid()) // ID type column whose default value is cuid
createdAt DateTime @default(now()) // Date type column whose default value is current date and time
modifiedAt DateTime @default(now()) // Date type column whose default value is current date and time
text String // String type column
options String[] // Column value should be a list of string
answer String // String type column
marks Int @default(0) // Integer type column whose default value is null
negativeMarking Boolean @default(false) // Boolean type value whose default value is false
}It is very important to remember there is never a prisma model relationship. Relationships are always both sided.
model User {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
email String @unique
projects Project[] // Model User has many relationship with Project model
name String?
}
model Project {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
users User[] // Model Project has many relationship with User model
name String
slug String @unique
stripeCustomerId String? @unique
stripeSubscriptionId String? @unique
stripePriceId String?
stripeCurrentPeriodEnd DateTime?
}model TestPaper {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
title String
description String
expireInDays Int @default(0)
maxAttempt Int @default(1)
negativeMarking Boolean @default(false)
totalTime Int @default(0)
results Result[] // Model TestPaper has many relationship on Result model
}
model Result {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
testId String
test TestPaper @relation(fields: [testId], references: [id]) // Model Result has one relationship with TestPaper model
score Int
}model User {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
email String @unique
projects Project[]
name String?
testsTaken Result[]
}
model Project {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
users User[]
name String
slug String @unique
stripeCustomerId String? @unique
stripeSubscriptionId String? @unique
stripePriceId String?
stripeCurrentPeriodEnd DateTime?
BigNum BigInt
}
model Question {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
text String
options String[]
answer String
marks Int @default(0)
papers TestPaper[]
}
model TestPaper {
id String @id @default(autoincriment())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
title String
description String
expireInDays Int @default(0)
maxAttempt Int @default(1)
questions Question[]
negativeMarking Boolean @default(false)
totalTime Int @default(0)
results Result[]
}
model Result {
id String @id @default(autoincriment())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
testId String
test TestPaper @relation(fields: [testId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
answers String[]
score Int
}/* eslint-disable no-return-await */
import {
extendType,
intArg,
list,
nonNull,
objectType,
stringArg,
} from "nexus";
import prisma from "../../db/prisma";
const Question = objectType({
name: "Question",
definition(t) {
t.nonNull.string("id"); // non-null string column with name `id`
t.nonNull.string("text"); // non-null string column with name `text`
t.nonNull.list.nonNull.string("options"); // non-nullable list of string column with name `options`
t.nonNull.string("answer"); // non-null string column with name `answer`
t.nonNull.int("marks"); // non-null integer column with name `marks`
t.nullable.int("expireInDays"); // nullable integer type column
t.nullable.int("maxAttempt"); // nullable integer type column
t.nullable.boolean("negativeMarking"); // nullable boolean type column
t.nullable.int("totalTime"); // nullable integer type column
},
});(postgresql, mysql, sqlite, sqlserver, mongodb, cockroachdb)
$ prisma [command]
init Setup Prisma for your app
generate Generate artifacts (e.g. Prisma Client)
db Manage your database schema and lifecycle
migrate Migrate your database
studio Browse your data with Prisma Studio
format Format your schema
--preview-feature Run Preview Prisma commands
prisma init --datasource-provider sqlite prisma init --url mysql://user:password@localhost:3306/mydb
$ prisma generate
$ prisma studio
Create migrations from your Prisma schema, apply them to the database, generate artifacts (e.g. Prisma Client)
$ prisma migrate dev
$ prisma db pull
$ prisma db push
prisma generate prisma generate --schema=./alternative/schema.prisma prisma generate --watch import { PrismaClient } from '@prisma/client'
prisma format
The db pull command connects to your database and adds Prisma models to your Prisma schema that reflect the current database schema.
prisma db pull prisma db pull --schema=./alternative/schema.prisma prisma db pull --print
prisma db push --accept-data-loss npx prisma db push --schema=/tmp/schema.prisma
prisma db seed
This command applies a SQL script to the database without interacting with the Prisma migrations table. The script takes two inputs: the SQL script, which can be provided either on standard input or in a file the data source, which can either be the URL of the data source or the path to your Prisma schema file
prisma db execute --file ./script.sql --schema schema.prisma $ echo 'TRUNCATE TABLE dev;' | prisma db execute --stdin --url="$DATABASE_URL"
The migrate dev command updates your database using migrations during development and creates the database if it does not exist. prisma migrate dev --name init prisma migrate dev --create-only
This command deletes and recreates the database, or performs a 'soft reset' by removing all data, tables, indexes, and other artifacts prisma migrate reset
The migrate deploy command applies all pending migrations, and creates the database if it does not exist. Primarily used in non-development environments. This command: prisma migrate deploy
The migrate resolve command allows you to solve migration history issues in production by marking a failed migration as already applied (supports baselining) or rolled back.
prisma migrate resolve --applied 20201231000000_add_users_table prisma migrate resolve --rolled-back 20201231000000_add_users_table
The prisma migrate status command looks up the migrations in /prisma/migrations/* folder and the entries in the _prisma_migrations table and compiles information about the state of the migrations in your database.
prisma migrate status
This command compares two database schema sources and outputs a description of a migration taking the first to the state of the second.
prisma migrate diff --from-... --to-...
prisma migrate diff
--from-url "$DATABASE_URL" \
--to-url "postgresql://login:password@localhost:5432/db2"
prisma migrate diff \
--from-url "$DATABASE_URL"
--to-migrations ./migrations
--script > script.sql
Start Studio on the default port and open a new browser tab to it
prisma studio --port 7777
You must first define the model then create mutation for the model.
const mutations = extendType({
type: "Mutation",
definition: (t) => {
t.nullable.field("createQuestion", {
type: "Question",
args: {
text: nonNull(stringArg()),
answer: nonNull(stringArg()),
marks: nonNull(intArg()),
options: nonNull(list(nonNull("String"))),
},
// Create data in model from args
resolve: async (_, args, ctx) => {
return await prisma.question.create({
data: {
text: args.text,
answer: args.answer,
marks: args.marks,
options: args.options,
},
});
},
});
t.nullable.field("updateQuestion", {
type: "Question",
args: {
id: nonNull(stringArg()),
text: nonNull(stringArg()),
answer: nonNull(stringArg()),
marks: nonNull(intArg()),
options: nonNull(list(nonNull("String"))),
},
// Updates data in question model by ID
resolve: async (_, args, ctx) => {
return await prisma.question.update({
where: { id: args.id },
data: {
text: args.text,
answer: args.answer,
marks: args.marks,
options: args.options,
},
});
},
});
},
});Graphql mutation query for above mutation will look like below:
mutation CreateQuestion(
$text: String!
$answer: String!
$marks: Int!
$options: [String!]!
$topicId: ID
) {
createQuestion(
text: $text
answer: $answer
marks: $marks
options: $options
topicId: $topicId
) {
id
}
}mutation UpdateQuestion(
$id: String!
$text: String!
$answer: String!
$marks: Int!
$options: [String!]!
) {
updateQuestion(
id: $id
text: $text
answer: $answer
marks: $marks
options: $options
) {
id
}
}import {
arg,
booleanArg,
extendType,
intArg,
list,
nonNull,
objectType,
stringArg,
} from "nexus";
const queries = extendType({
type: "Query",
definition: (t) => {
t.field("question", {
type: "Question",
args: {
id: nonNull(stringArg()),
},
// Query data from question model by ID from args
resolve: (_, { id }: any, ctx) => {
return prisma.question.findUnique({
where: {
id,
},
});
},
});
},
});The GraphQuery query object for above will look like below:
query GetQuestion($id: String!) {
question(id: $id) {
id
text
options
answer
marks
}
}Here the question in query is same as name of field in our query definition.
const queries = extendType({
type: "Query",
definition: (t) => {
// get all papers
t.list.field("papers", {
type: "TestPaper",
resolve: (_root, _args, ctx) => {
if (!ctx.user?.id) return null;
return prisma.testPaper.findMany();
},
});
},
});The GraphQuery query object for above will look like below:
query GetPaperList {
papers {
id
title
description
expireInDays
maxAttempt
negativeMarking
totalTime
}
}Here the papers in query is same as name of field in our query definition.
import {
arg,
booleanArg,
extendType,
intArg,
list,
nonNull,
objectType,
stringArg,
} from "nexus";
import prisma from "../../db/prisma";
const TestPaper = objectType({
name: "TestPaper",
definition(t) {
t.nonNull.string("id");
t.nonNull.string("title");
t.nonNull.string("description");
t.nullable.int("expireInDays");
t.nullable.int("maxAttempt");
t.nullable.boolean("negativeMarking");
t.nullable.int("totalTime");
// questions field will be of type question ID
// which will then be resolved from question table records using ID. It will be a list of ID
t.nonNull.list.nonNull.field("questions", {
type: "Question",
resolve: (parent, _, ctx) =>
ctx.prisma.testPaper
.findUnique({ where: { id: parent.id } })
.questions(),
});
},
});The prisma model for above model will look like below:
model TestPaper {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
title String
description String
expireInDays Int @default(0)
maxAttempt Int @default(1)
questions Question[]
negativeMarking Boolean @default(false)
totalTime Int @default(0)
}const Question = objectType({
name: "Question",
definition(t) {
t.nonNull.string("id");
t.nonNull.string("text");
t.nonNull.list.nonNull.string("options");
t.nonNull.string("answer");
t.nonNull.int("marks");
// topicId field will be of type topic ID
// which will then be resolved from topic table records
t.nonNull.field("topic", {
type: "Topic",
// eslint-disable-next-line max-len
resolve: (parent: any, _: any, ctx: any) =>
ctx.prisma.question.findUnique({ where: { id: parent.id } }).topic(),
});
},
});The prisma model for above model will look like below:
model Question {
id String @id @default(cuid())
createdAt DateTime @default(now())
modifiedAt DateTime @default(now())
text String
options String[]
answer String
marks Int @default(0)
papers TestPaper[]
topicId String
topic Topic @relation(fields: [topicId], references: [id])
}Nexus mutation of above model will look like below:
const mutations = extendType({
type: "Mutation",
definition: (t) => {
t.nullable.field("createQuestion", {
type: "Question",
args: {
text: nonNull(stringArg()),
answer: nonNull(stringArg()),
marks: nonNull(intArg()),
options: nonNull(list(nonNull("String"))),
// topic ID of type ID which links to topic in model
topicId: arg({
type: "ID",
}),
},
resolve: async (_, args: any, ctx) => {
if (!ctx.user?.id) return null;
return await prisma.question.create({
data: {
text: args.text,
answer: args.answer,
marks: args.marks,
options: args.options,
topic: {
connect: { id: args.topicId } || null,
},
},
});
},
});
t.nullable.field("updateQuestion", {
type: "Question",
args: {
id: nonNull(stringArg()),
text: nonNull(stringArg()),
answer: nonNull(stringArg()),
marks: nonNull(intArg()),
options: nonNull(list(nonNull("String"))),
},
resolve: async (_, args, ctx) => {
if (!ctx.user?.id) return null;
return await prisma.question.update({
where: { id: args.id },
data: {
text: args.text,
answer: args.answer,
marks: args.marks,
options: args.options,
},
});
},
});
},
});const mutations = extendType({
type: "Mutation",
definition: (t) => {
t.nullable.field("createTestPaper", {
type: "TestPaper",
args: {
title: nonNull(stringArg()),
description: nonNull(stringArg()),
expireInDays: intArg(),
maxAttempt: intArg(),
negativeMarking: booleanArg(),
totalTime: intArg(),
// Questions will contain any array of ID
questions: arg({
type: list("ID"),
}),
},
resolve: async (_, args: any, ctx) => {
if (!ctx.user?.id) return null;
return await prisma.testPaper.create({
data: {
title: args.title,
description: args.description,
expireInDays: args.expireInDays || 0,
maxAttempt: args.maxAttempt || 1,
negativeMarking: args.negativeMarking || false,
totalTime: args.totalTime || 0, // 0 = infinite time
// connecting question ID list in args with question table records
questions: {
connect: args.questions.map((que) => ({ id: que })) || [],
},
},
});
},
});
},
});Graphql mutation query for above mutation will look like below:
mutation CreateTestPaper(
$title: String!
$description: String!
$expireInDays: Int
$maxAttempt: Int
$negativeMarking: Boolean
$totalTime: Int
$questions: [ID!]!
) {
createTestPaper(
title: $title
description: $description
expireInDays: $expireInDays
maxAttempt: $maxAttempt
negativeMarking: $negativeMarking
totalTime: $totalTime
questions: $questions
) {
id
}
}Nexus object model for the data model:
const TestPaper = objectType({
name: "TestPaper",
definition(t) {
t.nonNull.string("id");
t.nonNull.string("title");
t.nonNull.string("description");
t.nullable.int("expireInDays");
t.nullable.int("maxAttempt");
t.nullable.boolean("negativeMarking");
t.nullable.int("totalTime");
// questions field will be of type question ID
// which will then be resolved from question table records
t.nonNull.list.nonNull.field("questions", {
type: "Question",
resolve: (parent, _, ctx) =>
ctx.prisma.testPaper
.findUnique({ where: { id: parent.id } })
.questions(),
});
},
});const queries = extendType({
type: "Query",
definition: (t) => {
// get all papers
t.list.field("papers", {
type: "TestPaper",
resolve: (_root, _args, ctx) => {
if (!ctx.user?.id) return null;
// Include questions in query response
return prisma.testPaper.findMany({
include: { questions: true },
});
},
});
// Get paper by ID with question
t.field("paperWithQuestion", {
type: "TestPaper",
args: {
id: nonNull(stringArg()),
},
resolve: (_, { id }: any, ctx) => {
if (!ctx.user?.id) return null;
// Include questions in query response
return prisma.testPaper.findUnique({
where: {
id,
},
include: { questions: true },
});
},
});
},
});Graphql query for above query object will look like below:
Return only question ID list with query response along with test paper details:
query GetPaperList {
papers {
id
title
description
expireInDays
maxAttempt
negativeMarking
totalTime
questions {
id
}
}
}Return question info list with query response along with test paper details:
query GetPaperWithQuestion($id: String!) {
paperWithQuestion(id: $id) {
id
title
description
expireInDays
maxAttempt
negativeMarking
totalTime
questions {
id
text
options
answer
marks
}
}
}