michallytek / typegraphql-prisma Goto Github PK
View Code? Open in Web Editor NEWPrisma generator to emit TypeGraphQL types and CRUD resolvers from your Prisma schema
Home Page: https://prisma.typegraphql.com
License: MIT License
Prisma generator to emit TypeGraphQL types and CRUD resolvers from your Prisma schema
Home Page: https://prisma.typegraphql.com
License: MIT License
I'm running into an issue with type conflicts when using doc lines in schema.prisma to change field names, where it looks like the type that prisma returns from create/find/etc doesn't match up with the type that is generated by typegraphql-prisma.
I am using typegraphql-prisma 0.8.0, prisma-cli 2.8.1 and prisma client 2.8.1
Here is my schema.prisma (I removed the unrelated models for brevity)
generator client {
provider = "prisma-client-js"
}
generator typegraphql {
provider = "typegraphql-prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Course {
id Int @id @default(autoincrement())
title String
subject String
headline String
intro String
/// @TypeGraphQL.field(name: "creatorId")
creator_id Int
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
creator User @relation(fields: [creator_id], references: [id])
enrolledUsers CoursesUsers[]
lessons Lesson[]
@@map("course")
}
model CoursesUsers {
/// @TypeGraphQL.field(name: "courseId")
course_id Int
/// @TypeGraphQL.field(name: "userId")
user_id Int
course Course @relation(fields: [course_id], references: [id])
user User @relation(fields: [user_id], references: [id])
@@id([course_id, user_id])
@@map("courses_users")
}
model User {
id Int @id @default(autoincrement())
username String @unique
email String @unique
/// @TypeGraphQL.omit(output: true)
password String?
googleId String? @map("google_id")
facebookId String? @map("facebook_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
createdCourses Course[]
enrolledCourses CoursesUsers[]
lessons Lesson[]
notes Note[]
@@map("user")
}
And here is where I am trying to use the generated type but am running into a conflict (the service is just a function called by a controller which is the actual resolver)
import { User, Course } from "@generated/type-graphql";
import { PrismaClient } from "@prisma/client";
export const createCourseService = async (
prisma: PrismaClient,
createCourseInput: CreateCourseInput,
userId: number
): Promise<Course> => {
let err, newCourse: Course | undefined, foundUser;
const { course, user } = prisma;
[err, foundUser] = await to(user.findOne({ where: { id: userId } }));
if (err) throw new Error(err.message);
if (!foundUser)
throw new AuthenticationError("Could not find user to create course");
[err, newCourse] = await to(
course.create({
data: {
...createCourseInput,
creator: {
connect: { id: userId },
},
},
})
);
if (err) throw new Error(err.message);
if (!newCourse) {
throw new ApolloError("Internal server error. Could not create newCourse");
}
return newCourse;
};
So my error message says: Type 'import("c:/Users/Joel/Documents/webprojects/serious/Smarterish/code/server/node_modules/.prisma/client/index").Course | undefined' is not assignable to type 'import("c:/Users/Joel/Documents/webprojects/serious/Smarterish/code/server/node_modules/@generated/type-graphql/models/Course").Course | undefined'.
Property 'creatorId' is missing in type 'import("c:/Users/Joel/Documents/webprojects/serious/Smarterish/code/server/node_modules/.prisma/client/index").Course' but required in type 'import("c:/Users/Joel/Documents/webprojects/serious/Smarterish/code/server/node_modules/@generated/type-graphql/models/Course").Course'.
I'm not sure if it's an actual issue or if I should mark the return type as Prisma.Course rather than Course from typegraphql or what.
When prisma is not returned as part of the context callback in the ApolloServer constructor, a generic "cannot get x of type undefined" is returned whenever a graphql query is run. This is documented in the README, however, a more helpful error message could go a long way here. I lost a fair amount of time debugging this issue until I found the relevant documentation.
Essentially, check if prisma exists on the context object, and if it doesn't, throw a more helpful error.
I have a schema that is using @id @default(cuid())
as ID column, but TypeGraphQL is setting this field as a String, not as a ID.
Is there any way to tell TypeGraphQL to set the id field type of the prisma schema to the ID Type?
I'd like to write unit tests for resolvers in an app that uses this package. I've been following the guidance of this issue on writing tests:
MichalLytek/type-graphql#231 (comment)
Does this model also work in an environment with Prisma? What extra steps are necessary to be able to test custom resolvers, if any? Would they be the same as if we were testing TypeGraphQL resolvers without Prisma?
Fantastic work on transpiling @MichalLytek.
I was thinking about how we could tackle the circular imports without needing to use commonjs
on everything.
I was wondering if bundling all the transpiled code into a single file would be possible 🤔
Bundling would also reduce the size if only by removing all the import statements which in turn may reduce write time to disk as there is less to write and it is all in only a few files rather then what could be up to hundreds.
If we can reduce generation times that would be great as then we could also improve testing speed.
Originally posted by @wSedlacek in #1 (comment)
Happens because generator.js resides in
./node_modules/typegraphql-prisma/lib/generator.js
not in
./node_modules/typegraphql-prisma/generator.js
package.json:
"@prisma/cli": "^2.10.1",
"@prisma/client": "^2.10.1",
"typegraphql-prisma": "^0.8.3"
output:
Error: Generator at node ./node_modules/typegraphql-prisma/generator.js could not start:
internal/modules/cjs/loader.js:968
throw err;
^
Error: Cannot find module '/path/to/project/node_modules/typegraphql-prisma/generator.js'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:965:15)
at Function.Module._load (internal/modules/cjs/loader.js:841:27)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
at internal/main/run_main_module.js:17:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
Hello, this is a question, not an issue.
I would like to use all of this, typeorm and this project all within the browser, without a nodejs server.
Having established that I can use typeorm from within the browser with the database within the browser and run an example code which does that, then I want to use this tool typegraphql-prisma to generate the files and then from prisma and do graphql queries.
I've tried to embed ApolloServer within the web browser using webpack but failed to do so, it seems to use too many nodejs dependencies which will not work in the browser via webpack.
So the question is, can you give any advice how I could go about getting all of this to work within the web browser? Being able to store state within the database within the web browser and applying GraphQL queries on that is my goal.
could I run a graphql query using typegraphql in the browser using the outputs of this prisma typegraphql project? I mean using the files outputted from the prisma
Hello, i'm using the Prisma/cli and prisma/client version 2.12.1. i'm using these because i hava Bytea fields in psql and the introspection convert it to Bytes.
This scalar type isn't an option on helpers.js file.
Could you tell me what can i fix it.
Thanks in advance.
I have this model (simplified version):
model User {
id String @id @default(cuid())
name String
memberOfProjects UsersOnProjects[]
ownsProjects Project[]
}
model Project {
id String @id @default(cuid())
owner User @relation(fields: [ownerId], references: [id])
ownerId String
members UsersOnProjects[]
}
model UsersOnProjects {
project Project @relation(fields: [projectId], references: [id])
projectId String
user User @relation(fields: [userId], references: [id])
userId String
createdAt DateTime @default(now())
@@id([userId, projectId])
}
After the typegraphql generation I have this relations resolver:
import * as TypeGraphQL from "type-graphql";
import { Project } from "../../../models/Project";
import { User } from "../../../models/User";
import { UsersOnProjects } from "../../../models/UsersOnProjects";
@TypeGraphQL.Resolver(_of => UsersOnProjects)
export class UsersOnProjectsRelationsResolver {
@TypeGraphQL.FieldResolver(_type => Project, {
nullable: false
})
async project(@TypeGraphQL.Root() usersOnProjects: UsersOnProjects, @TypeGraphQL.Ctx() ctx: any): Promise<Project> {
return ctx.prisma.usersOnProjects.findUnique({
where: {
projectId_userId: {
projectId: usersOnProjects.projectId,
userId: usersOnProjects.userId,
},
},
}).project({});
}
@TypeGraphQL.FieldResolver(_type => User, {
nullable: false
})
async user(@TypeGraphQL.Root() usersOnProjects: UsersOnProjects, @TypeGraphQL.Ctx() ctx: any): Promise<User> {
return ctx.prisma.usersOnProjects.findUnique({
where: {
projectId_userId: {
projectId: usersOnProjects.projectId,
userId: usersOnProjects.userId,
},
},
}).user({});
}
}
Then I am trying to query this:
query project($id: String!) {
project(where: {id: $id}) {
id
createdAt
members(where: {projectId: {equals: $id}}) {
user {
name
id
}
}
content
}
}
But apollo returns an error:
{
where: {
projectId_userId: {
~~~~~~~~~~~~~~~~
projectId: 'somerandomid',
userId: 'anotherrandomid'
}
},
select: {
user: {}
}
}
Unknown arg `projectId_userId` in where.projectId_userId for type UsersOnProjectsWhereUniqueInput. Did you mean `userId_projectId`? Available args:
type UsersOnProjectsWhereUniqueInput {
userId_projectId?: UsersOnProjectsUserIdProjectIdCompoundUniqueInput
}
Changing the queried field in the generated relations resolver accordingly to userId_projectId
instead of projectId_userId
fixes the issue, but of course after regeneration the error pops up again.
I am not quite sure why it has to be a specific order, but I guess the generator has to be fixed to swap the order of the fields in the relation.
We are writing an E2E test with dev
version of Prisma and typegraphql-prisma
. The goal is to detect any breaking changes from Prisma's end in typegraphql-prisma
as early as possible.
Today this is not possible because validation returns
Error: Looks like you use an incorrect version of the Prisma packages: "2.12.0-dev.8". Please ensure that you have installed a version that meets 'typegraphql-prisma' requirement: "~2.11.0".
It would be great to have an env var like VALIDATE_PRISMA_VERSION=<boolean>
to bypass this check.
I have a simple app with the following Prisma schema:
model TodoViewTodo {
id Int @id @default(autoincrement())
todo Int
todoView Int
placeholder String?
Todo Todo @relation(fields: [todo], references: [id])
TodoView TodoView @relation(fields: [todoView], references: [id])
}
model Todo {
id Int @id @default(autoincrement())
text String
done Boolean?
user Int
User User @relation(fields: [user], references: [id])
TodoViewTodo TodoViewTodo[]
}
model TodoView {
id Int @id @default(autoincrement())
label String
order String?
color String
TodoViewTodo TodoViewTodo[]
}
When I generate a schema using typegraphql-prisma
resolvers and buildSchema
I get an empty input:
input TodoViewTodoUpdateManyMutationInput
which causes all GraphQL operations to fail due to an invalid schema.
I had to add the placeholder
field for this input not to be empty, which is not ideal.
Originally posted by @J718 in #1 (comment)
"type-graphql": "^1.0.0",
typegraphql-prisma": "^0.6.1",
I keep getting errors about accessing inputs before initialization and wanted to get your input. The error is as follows:
ReferenceError: Cannot access 'BankAccountWhereInput' before initialization
at Module.BankAccountWhereInput (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:11490:113)
at Module../src/prisma/generated/type-graphql/resolvers/inputs/BankAccountListRelationFilter.ts (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:10345:110)
at __webpack_require__ (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:23:31)
at Module../src/prisma/generated/type-graphql/resolvers/inputs/BankConnectionWhereInput.ts (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:13159:95)
at __webpack_require__ (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:23:31)
at Module../src/prisma/generated/type-graphql/resolvers/inputs/BankAccountWhereInput.ts (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:11499:90)
at __webpack_require__ (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:23:31)
at Module../src/prisma/generated/type-graphql/resolvers/crud/BankAccount/args/AggregateBankAccountArgs.ts (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:3695:87)
at __webpack_require__ (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:23:31)
at Module../src/prisma/generated/type-graphql/resolvers/crud/BankAccount/BankAccountCrudResolver.ts (C:\Users\jacob\OneDrive\Documents\GitHub\tictactoe\.next\server\pages\api\graphql.js:3208:88)
I have the following schema.prisma file.
generator client {
provider = "prisma-client-js"
}
generator typegraphql {
provider = "node ../node_modules/typegraphql-prisma/generator.js"
output = "../prisma/generated/type-graphql"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Account {
id Int @default(autoincrement()) @id
compoundId String @unique @map(name: "compound_id")
userId Int @map(name: "user_id")
providerType String @map(name: "provider_type")
providerId String @map(name: "provider_id")
providerAccountId String @map(name: "provider_account_id")
refreshToken String? @map(name: "refresh_token")
accessToken String? @map(name: "access_token")
accessTokenExpires DateTime? @map(name: "access_token_expires")
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
@@index([providerAccountId], name: "providerAccountId")
@@index([providerId], name: "providerId")
@@index([userId], name: "userId")
@@map(name: "accounts")
}
model BankAccount {
account_id String @id
bankConnectionId Int @map(name: "bank_connection_id")
balancesAvailable Float @map(name: "balances_available")
balancesCurrent Float @map(name: "balances_current")
isoCurrencyCode String @map(name: "iso_currency_code")
name String
officialName String @map(name: "official_name")
subtype String
type String
bankConnection BankConnection @relation(fields: [bankConnectionId], references: [id])
@@map(name: "bank_account")
}
model BankConnection {
id Int @default(autoincrement()) @id
user User @relation(fields: [userId], references: [id])
userId Int @map(name: "user_id")
itemId String @map(name: "item_id")
accessToken String @map(name: "access_token")
bankAccounts BankAccount[]
@@map(name: "bank_connection")
}
model Session {
id Int @default(autoincrement()) @id
userId Int @map(name: "user_id")
expires DateTime
sessionToken String @unique @map(name: "session_token")
accessToken String @unique @map(name: "access_token")
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
user User @relation(fields: [userId], references: [id])
@@map(name: "sessions")
}
model User {
id Int @default(autoincrement()) @id
name String?
email String? @unique
emailVerified DateTime? @map(name: "email_verified")
image String?
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
bankConnections BankConnection[]
sessions Session[]
@@map(name: "users")
}
model VerificationRequest {
id Int @default(autoincrement()) @id
identifier String
token String @unique
expires DateTime
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @default(now()) @map(name: "updated_at")
@@map(name: "verification_requests")
}
Here is my build schema command
const schema = await buildSchema({
// resolvers: [CreateLinkTokenResolver, UserCrudResolver, GameCrudResolver, UserRelationsResolver, GameRelationsResolver],
resolvers: [LinkTokenResolver, UserRelationsResolver ],
authChecker: customAuthChecker,
validate: false,
})
Describe the Bug
Yesterday I downgraded prisma from 2.11.0 to 2.10.2 to be able to use with your marvellous library. This morning I have seen your update to 0.8.4 and I have upgrade my prisma version to 2.11.0 again and update yours. And when I try to generate the code it throws this error. Object.fromEntries is not a function
To Reproduce
prisma generate
Environment variables loaded from D:\project\prisma\.env
Prisma schema loaded from prisma\schema.prisma
Error:
✔ Generated Prisma Client (version: 2.11.0) to .\node_modules\@prisma\client in 188ms
TypeError: Object.fromEntries is not a function
at Object.generateEnhanceMap (D:\project\node_modules\typegraphql-prisma\lib\generator\enhance.js:20:63)
at Object.generateCode [as default] (D:\project\node_modules\typegraphql-prisma\lib\generator\generate-code.js:158:15)
at Object.generate [as onGenerate] (D:\project\node_modules\typegraphql-prisma\lib\cli\prisma-generator.js:32:34)
Environment (please complete the following information):
Thanks for all your work!
When having a Prisma schema like this:
model Plan {
zipCodes String[]
}
And then generate the corresponding types with [email protected]
it generates the following model:
@TypeGraphQL.ObjectType({
isAbstract: true,
description: undefined,
simpleResolvers: undefined,
})
export class Plan {
@TypeGraphQL.Field(_type => [String], {
nullable: true,
description: undefined,
})
zipCodes?: string[] | null;
}
But since list field types cannot be set as optional in Prisma, shouldn't the resulting model be like this?
@TypeGraphQL.ObjectType({
isAbstract: true,
description: undefined,
simpleResolvers: undefined,
})
export class Plan {
@TypeGraphQL.Field(_type => [String], {
nullable: false,
description: undefined,
})
zipCodes!: string[];
}
This is a simple example to illustrate the situation we are experiencing with lists/arrays
Using:
@prisma/[email protected]
@prisma/[email protected]
[email protected]
For example: I wrote custom subscription method in a UserCrudResolver.
It would be useful if there would be a way to map certain primitive field scalars from certain types of the prisma schema to custom graphql scalars for example with graphql-scalars to have an even stricter type system which also reduces the need for class-validator since the validation takes place directly at the resolver level. It would be really nice to have this feature to have more control over the schema generation.
type definition of Person
in prisma.schema
with the primitive type String
:
model Person {
email String
}
type definition of Person
in schema.graphql
with a custom scalar EmailAddrress
:
type Person {
email: EmailAddress
}
generated type definition of Person
as a typegraphql object type with custom scalar resolver:
import { EmailAddressResolver } from 'graphql-scalars';
@ObjectType()
export class Person {
@Field(() => EmailAddressResolver)
email!: string;
}
I have a property status
(ENUM) which seems to cause compatibility issues, so I'm required to use Prisma Client's model instead of the one generated by type-graphql. Do you know what this might be?
If I use the CatalogModel then it works fine, but when I use the one generated by type-graphql, I get the above issue.
Originally posted by @jsefiani in #1 (comment)
Thank you for the amazing library. When running prisma generate inside docker build command, I am getting below error. Though, prisma client is able to generate the code but not typegraphql-prisma.
> prisma generate
Prisma schema loaded from prisma/schema.prisma
Error:
TypeError: Cannot read property 'datamodel' of undefined
at Object.generateCode [as default] (/home/app/node_modules/typegraphql-prisma/lib/generator/generate-code.js:37:29)
at Object.generate [as onGenerate] (/home/app/node_modules/typegraphql-prisma/lib/cli/prisma-generator.js:32:34)
at async LineStream.<anonymous> (/home/app/node_modules/@prisma/generator-helper/dist/generatorHandler.js:13:32)
✔ Generated Prisma Client (version: 2.11.0) to ./node_modules/@prisma/client in 277ms
Here is my Dockerfile.
FROM node:lts AS base
WORKDIR /home/app
COPY package*.json ./
RUN npm install
COPY src src
COPY prisma prisma
COPY tsconfig.json tsconfig.json
RUN npm run generate:prisma
Here is my schema.prisma file header.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator typegraphql {
provider = "typegraphql-prisma"
useOriginalMapping = true
}
generator client {
provider = "prisma-client-js"
}
I have a simple app with the following Prisma schema:
model TodoViewTodo {
id Int @id @default(autoincrement())
todo Int
todoView Int
placeholder String?
Todo Todo @relation(fields: [todo], references: [id])
TodoView TodoView @relation(fields: [todoView], references: [id])
}
model Todo {
id Int @id @default(autoincrement())
text String
done Boolean?
user Int
User User @relation(fields: [user], references: [id])
TodoViewTodo TodoViewTodo[]
}
model TodoView {
id Int @id @default(autoincrement())
label String
order String?
color String
TodoViewTodo TodoViewTodo[]
}
There should be a way to get those relational fields, like:
query {
todoViews {
id
TodoViewTodo {
id
}
}
}
It is possible to filter the result using relational fields:
query {
todoViews(
where: {
TodoViewTodo: { every: { Todo: { is: { done: { equals: true } } } } }
}
) {
id
}
}
so I think it would be only consistent if it was possible to select those fields too.
I'm thinking about migrating from Prisma 1 to Prisma 2 + TypeGraphQL, but just found that generated GraphQL schema (see e.g. generated-schema.graphql) is not Relay-compliant, e.g. there is no the interface called Node
.
Is it comes from Prisma 2 or TypeGraphQL? Is it fixable?
From #1 (comment):
And in the near feature - when Prisma SDK will be ready - the
typegraphql-prisma
integration will also allow to use a code-first approach to build aschema.prisma
and GraphQL schema at once, using classes with decorators as a single source of truth. Stay tuned! 💪
Are we there yet? Is the Prisma SDK ready? If not, what exactly is missing?
Recently I've been chatting with @divyenduz and he asked to open issue(s) about this in Prisma repo(s), so the Prisma team would have them in mind.
Start the server with ts-node and everything works normally.
Is there any thing that ApplyModelEhanceMap relies on?
The function to apply decorators to resolvers works fine.
import "reflect-metadata";
import {
resolvers,
applyModelsEnhanceMap,
ModelsEnhanceMap,
} from "@generated/type-graphql";
import { buildSchema } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { PrismaClient } from "@prisma/client";
import { Authorized, AuthChecker } from "type-graphql";
const modelsEnhanceMap: ModelsEnhanceMap = {
User: {
fields: {
firstName: [Authorized(["ADMIN"])],
},
},
};
const customAuthChecker: AuthChecker<Context> = (
{ root, args, context, info },
roles
) => {
throw ""; // Never Executed
return false; // or false if access is denied
};
applyModelsEnhanceMap(modelsEnhanceMap);
interface Context {
prisma: PrismaClient;
}
async function main() {
const prisma = new PrismaClient();
await prisma.$connect();
const schema = await buildSchema({
resolvers: resolvers,
authChecker: customAuthChecker,
authMode: "error",
validate: true,
});
const server = new ApolloServer({
schema,
playground: true,
context: (): Context => ({ prisma }),
});
const { port } = await server.listen(4000);
console.log(`GraphQL is listening on ${port}!`);
}
export = {
name: "api2",
mixins: [],
async started() {
try {
await main();
} catch (e) {
console.error(e);
}
},
};
// Uncomment and execute the file with ts-node, everything works normally.
// (async () => {
// try {
// await main();
// } catch (e) {
// console.error(e);
// }
// })();
This issue was transferred from the main repo. Be aware that some links in this discussion might be outdated.
I am thrilled to present the first preview of the upcoming TypeGraphQL & Prisma Framework integration 🎉
You can install it right now - it's distributed as a package typegraphql-prisma
on NPM:
https://www.npmjs.com/package/typegraphql-prisma
All the docs (in readme), examples and the source code of the integration are located on the prisma
branch of this repo (in the future it will be moved into a master
branch when I finish converting the project to a monorepo):
https://github.com/MichalLytek/type-graphql/tree/prisma
Currently released version 0.x
is just a preview - it lacks some customization option, like picking/omitting fields of object types to expose in the schema, as well as picking CRUD methods and exposed args. However, the base functionality is working well, so I strongly encourage you to give it a try and play with it.
Any feedback about the developers experience, bug reports or ideas about new features or enhancements are very welcome. However, during the preview I would ask to use the dedicated Gitter room if you have any questions:
https://gitter.im/type-graphql/prisma2
If you have a bug report, feel free to add comments only in this issue, not to create a new issues, as I don't want to pollute the repo for now 😉
And in the near feature - when Prisma SDK will be ready - the typegraphql-prisma
integration will also allow to use a code-first approach to build a schema.prisma
and GraphQL schema at once, using classes with decorators as a single source of truth. Stay tuned! 💪
I had an idea for another possible feature.
Union Resolvers. Adding /// @TypeGraphql.Union = result
to two models will generate an additional set of findOne/findMany revolvers of a union type of each model that is in that union.
An example may look like this.
/// @TypeGraphql.Union = product
model Movie {
id Int @id @default(autoincrement())
title String
length Int
actors Actor[]
}
/// @TypeGraphql.Union = product
model Book {
id Int @id @default(autoincrement())
title String
pages Int
}
model Actor {
id Int @id @default(autoincrement())
name String
}
Which would generate this type and the resolvers for it.
export const Product = createUnionType({
name: 'Product',
types: () => [Movie, Book],
});
The Prisma code for this may look something like this.
const movies = await prisma.movies.findMany(args).then((movies: Movie[]) => plainToClass(Movie, movies));
const books = await prisma.books.findMany(args).then((books: Book[]) => plainToClass(Book, books));
return [...movies, ...books].sort(byArgs(args));
The difficult part would be defining the orderBy
and where
arguments as you would need to determine what fields were shared between the two (or more) models and use those to generate the arguments.
Additionally sorting will likely have some additional overhead but it would be cool to be able to do something like this.
{
products {
... on Book {
title
pages
}
... on Movie {
title
length
}
}
}
Originally posted by @wSedlacek in #1 (comment)
Package versions:
Description:
I created the following many-to-many relationship in my PostgreSQL database using Prisma:
When I query the products, I also want to query its categories. To make this possible I created the following field resolver that returns the type CategoriesConnection
:
When inspecting my schema using the GraphQL playground, I see that the type of categories
is not overwritten by the type that I provided, it remains [Category!]
(was automatically provided by Prisma):
Does my explanation make sense? If not I would be happy to provide you with more details.
Trying to upgrade from Prisma 2.5.1 to a newer version for quite a while with no luck so versions drifted already.
Now trying to upgrade:
"@prisma/cli": "^2.5.1",
"@prisma/client": "^2.5.1",
"typegraphql-prisma": "^0.5.0"
Where this (barebone version of the) code was generated:
class UpdateInput {
text?: string | undefined;
}
And this intuitive code used to work:
const data: UpdateInput = {
text: 'some text'
};
const text = data.text;
"@prisma/cli": "^2.11.0",
"@prisma/client": "^2.11.0",
"typegraphql-prisma": "^0.8.4"
Where the generated code got modified to:
class NullableStringFieldUpdateOperationsInput {
set?: string | undefined;
}
class UpdateInput {
text?: NullableStringFieldUpdateOperationsInput | undefined;
}
And this code doesn't work anymore:
const data: UpdateInput = {
text: 'some text'
};
const text = data.text;
Is it correct that at this point the code should be written in this less-intuitive way:
const data: UpdateInput = {
field: { set: 'some text' }
};
const text = data.text?.toString();
Or am I missing something and there's a way to use it intuitively?
Hi! After trying to adopt Prisma 2 and migrate my last few years of work to it I was little tired. Prisma 1 was really handy. Today I found this project and it is life saver. Michal excellent work!! Thank you!
My question is maybe the word of lazy dev but I is there a way to import all generated code into schema builder.
...
import * as typegraphql from "./prisma/generated/type-graphql";`
...
async function main() {
const schema = await buildSchema({
resolvers: [
typegraphql.resolvers
],
emitSchemaFile: path.resolve(__dirname, "./generated-schema.graphql"),
validate: false,
});
`
I could write anothe index.ts and export all the resolvers, but I guess that this could generated too?
Thank you again for your work!
How can I implement authorization scope for different users? In plain typegraphql a simple useMiddleware
function could solve it but is there a way to implement the same using typegraphql-prisma while still having the same query/mutation structure?
After excecute npx prisma generate we have some Input Types that's are empty, so, when i tried to run the nest app i have the following Error:
[Nest] 4976 - 2020-10-16 12:59:05 [ExceptionHandler] Some errors occurred while generating GraphQL schema:
Input Object type Sgp_ch_turnos_agenteUpdateManyDataInput must define one or more fields.
that's my auto generated file:
import * as TypeGraphQL from "type-graphql";
import GraphQLJSON from "graphql-type-json";
import { JsonValue, InputJsonValue } from "@prisma/client";
@TypeGraphQL.InputType({
isAbstract: true,
description: undefined,
})
export class Sgp_ch_turnos_agenteUpdateManyDataInput {
}
It would be awesome to have a bit more control over how the CRUD resolvers are generated. For example there could be more options to the field decorator to include authorization like so:
model User {
id Int @default(autoincrement()) @id
/// @TypeGraphQL.field(name: "emailAddress")
email String @unique
/// @TypeGraphQL.field(authorized: ["User", "Admin"])
posts Post[]
}
Is this something you consider as a feature? I think this would greatly increase development speed.
@MichalLytek I have been playing around with the code base and have been testing two new features that might be nice in typegraphql-prisma.
take
(required to properly determine complexity)Together these can prevent memory overflow high CPU utilization by limiting how complex queries can be which is extremely useful when you have large databases.
Originally posted by @wSedlacek in #1 (comment)
I've been searching and trying different approaches but can't find a way to apply authorization decorator to a relation resolver in a list operations, for example if I have the following schema:
model Book {
id Int
author Author @relation(fields: [authorId], references: [id])
authorId Int
}
model Author {
id Int
books Book[]
}
typegraphql-prisma
would then generate the resolvers/crud/Book/BookCrudResolver.ts
file which would contain a list resolver as follows:
@TypeGraphQL.Query(_returns => [Book], {
nullable: false
})
async books(@TypeGraphQL.Ctx() ctx: any, @TypeGraphQL.Args() args: FindManyBookArgs): Promise<Book[]> {
return ctx.prisma.book.findMany(args);
}
And a single entry resolver like this:
@TypeGraphQL.Query(_returns => Book, {
nullable: true
})
async book(@TypeGraphQL.Ctx() ctx: any, @TypeGraphQL.Args() args: FindUniqueBookArgs): Promise<Book | null> {
return ctx.prisma.book.findUnique(args);
}
And for the relationship with the User
there would be another file at resolvers/relations/Book/BookRelationsResolver.ts
which would contain a field resolver for the stablished one-to-many relationship with User
like this:
@TypeGraphQL.FieldResolver(_type => Author, {
nullable: false
})
async author(@TypeGraphQL.Root() book: Book, @TypeGraphQL.Ctx() ctx: any): Promise<Author> {
return ctx.prisma.book.findUnique({
where: {
id: book.id,
},
}).author({});
}
Now for the authorization part I have tried the following decorator mapping:
const modelsEnhanceMap: ModelsEnhanceMap = {
Book: {
fields: {
author: [Authorized()],
}
}
}
And it works fine for the single entry resolver book
but it doesn't work for the list resolver books
.
My questions is how to also apply the authorization to the listing? I suspect this might be a bug as it works in one side but not the other.
Using latest versions of typegraphql
, typegraphql-prisma
and prisma
The 2.8.1 prism has a problem that they solved in 2.9.0
/usr/bin/node[329084]: ../src/node_http_parser_impl.h:529:static void node::{anonymous}::Parser::Initialize(const v8::FunctionCallbackInfov8::Value&): Assertion `args[3]->IsInt32()' failed.
update your package so that you can use it
Describe the Bug
I have tables post | posttag | tag and code generated by prisma.
posttag table fields are postId, tagId. Its ok when I add any additional field.
node_modules\graphql\type\validate.js:69
throw new Error(errors.map(function (error) {
^
Error: Input Object type PosttagUpdateManyMutationInput must define one or more fields.
Environment (please complete the following information):
In the config passed to applyResolversEnhanceMap
, is there any way to pass in "wildcard" decorators that are applied to all fields of the type?
Additionally, a way to specify a "default" set of decorators that get applied to all fields would be nice.
Similar to how graphql-shield does it: https://github.com/maticzav/graphql-shield#per-type-wildcard-rule
I am trying to set up this package together with the nest-js package using a more modular approach than the provided example:
/// app.module.ts
interface Context {
prisma: PrismaClient;
}
const prisma = new PrismaClient();
@Module({
imports: [
TypeGraphQLModule.forRoot({
validate: false,
dateScalarMode: "timestamp",
playground: true,
introspection: true,
path: "/",
emitSchemaFile: true,
context: (): Context => ({ prisma })
})
],
controllers: [AppController],
providers: [
// Models
User,
Project,
// Relations
UserRelationsResolver,
ProjectRelationsResolver,
// Crud
UserCrudResolver,
ProjectCrudResolver,
// Custom
]
})
export class AppModule {
}
// main.ts
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
await app.listen(4000);
}
bootstrap();
Everything starts up fine and the graphql endpoint works as expected, but opening localhost:4000
in the browser doesn't open the playground. Is there something missing?
When I use yarn 2 to run basic example, it just hang in:
Prisma Schema loaded from prisma\schema.prisma
to reproduce:
yarn set version berry
yarn
on that directoryyarn prisma generate
prisma will hang generating typegraphql-prisma, but when I comment generator typegraphql
it runs fine.
Official prisma release is currently 2.8.1
, yet typegraphql-prisma requires 2.9.0-dev.36
Could typegraphql-prisma separate between stable releases and dev releases?
Is there a way to add custom decorators on top of prisma schema resolvers? e.g. I want for some crud operations to be available to certain roles of users. What is the approach I should use?
Originally posted by @reflash in #1 (comment)
How do I add authentication or the @Authorized
detective for generated resolves? If no is there a workaround?
Originally posted by @zifahm in #1 (comment)
Currently securing a graphql api using the first-class Authorized decorator and a custom authenticator hooked into Auth0.
In the current workflow there is a lot of copy-pasting on creation and changes in order to decorate the generated resolvers that are to be secure using the decorator.
As a feature suggestion, I think it would greatly reduce this manual process by adding a custom Prisma schema decorator on the model level for the CRUD operations, or wherever this would be technically feasible to do.
Originally posted by @MichaelHindley in #1 (comment)
For example
article -> article_tag -> tag
article(where:{id:id}) {
id
title
text
tags(data:{[
{title:1},
{title:2},
{title:3},
]}) {
id
title
}
}
We are writing a E2E test for typegraphql-prisma
generator. I ran into the following issue while doing that
Error: Error:
TypeError: Cannot read property 'output' of undefined
at Object.generate [as onGenerate] (/Users/divyendusingh/Documents/prisma/e2e-tests/community-generators/typegraphql-prisma/node_modules/typegraphql-prisma/lib/cli/prisma-generator.js:14:100)
package.json
{
"name": "typegraphql-prisma",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@prisma/cli": "2.11.0",
"typegraphql-prisma": "0.8.4"
},
"dependencies": {
"@prisma/client": "2.11.0"
}
}
schema.prisma
generator typegraphql {
provider = "typegraphql-prisma"
output = "./generated/typegraphql-prisma"
}
model User {
id String @id @default(cuid())
email String @unique
name String?
}
yarn; yarn prisma generate
Reproduction repo: https://github.com/divyendu-test/typegraphql-generator-issue
I've noticed that after generation I get empty relations/args.index.ts
file. It causes some bugs when compiling my ts project to js.
Error:
src/generated/typegraphql-prisma/resolvers/relations/index.ts:2:15 - error TS2306: File '.../src/generated/typegraphql-prisma/resolvers/relations/args.index.ts' is not a module.
2 export * from "./args.index";
Dependency versions:
[email protected]
@prisma/[email protected]
@prisma/[email protected]
Software projects usually have the 90% / 10% problem, where the 90% of the project usually are CRUD systems and the other 10% business logic.
The integration between TypeGraphQL and Prisma provided by the package typegraph-prisma
provides a code generation system that based in the schema from prisma.schema
emits a CRUD system in top of TypeGraphQL, having a single source of truth where Prisma is the main driver of the CRUD metadata and TypeGraphQL implements the surface API that interops with it.
So, now we have our first 90% of the project automatically generated with just the adition of Prisma + TypeGraphQL code generator, lets speak about that other 10%.
In this 10%, 9% of the code will be to provide business rules to the CRUD, as resources needs usually validation and authorization to be compliant with the business rules.
So, users of this library needs to implement in top of the basic CRUD provided by TypeGraphQL that 9% of the code required to follow the business rules.
This proposal tries to create a way of creating solutions for that 9% of the code and abstract it to the schema.prisma
as a series of features that would let the user have all the CRUD + rules generated as code from a single source of truth, so all the CRUD abstractions would get generated behind the scenes and only that final 1% of the business logic would need to be created, mantained and updated by the user.
To start working in that 9%, a way to extend TypeGraphQL code generation would be needed, and to not limitate users to opinioned validation and authorization it would need to be contained in to separate, importable packages, hence this RFC for a plugin system.
Proposer | Github | Organization |
---|---|---|
Fox Salva | @SrZorro | Intricom Resources SL |
Bernat Vadell | @bernatvadell | Intricom Resources SL |
Álvaro López | @zifiul | Intricom Resources SL |
Ian Ensenyat | @densenyat | Intricom Resources SL |
Currently typegraph-prisma
uses two ways of extending the default CRUD behaviour, one that its used only internaly by TypeGraphQL that the consumers of this library can't extend currently, and another that its decorator based, but in the current state only lets decorate the resolvers, so its not specific enough to have complete control of the CRUD resources, works for stuff like Authorized or not for this resource, but doesn't leave much space for let's say this field should be validated and if its not an email stop the user request with a validation error.
Example of a posible solution that given this plugin system, a plugin called typegraph-prisma-plugin-class-validator
adds validation to the CRUD.
schema.prisma
generator typegraphql {
provider = "node ../src/cli/dev.ts"
output = "../prisma/generated/type-graphql"
plugins = ["typegraph-prisma-plugin-class-validator"]
}
model User {
id Int @id @default(autoincrement())
/// @Validator.IsEmail()
email String @unique
/// @Validator.MaxLength(30)
/// @TypeGraphQL.field(name: "firstName")
name String?
/// @Validator.Min(value: 18, message: "You must be atleast $constraint1 years old")
age Int
}
Generated code at models/User.ts
from previous schema.prisma
import * as TypeGraphQL from "type-graphql";
import GraphQLJSON from "graphql-type-json";
import { JsonValue, InputJsonValue } from "../../../client";
import * as ClassValidator from "class-validator";
@TypeGraphQL.InputType({
isAbstract: true,
description: undefined,
})
export class UserCreateInput {
@ClassValidator.IsEmail()
@TypeGraphQL.Field(_type => String, {
nullable: false,
description: undefined
})
email!: string;
name?: string | undefined;
@ClassValidator.Min(18, {message: "You must be atleast $constraint1 years old"})
@TypeGraphQL.Field(_type => TypeGraphQL.Int, {
nullable: false,
description: undefined
})
age!: number;
@ClassValidator.MaxLength(30)
@TypeGraphQL.Field(_type => String, {
nullable: true,
description: undefined
})
get firstName() {
return this.name;
}
set firstName(name: string | undefined) {
this.name = name;
}
}
schema.prisma
plugin usageBecause schema.prisma
gets modified by prisma instrospect
we need a way to add our stuff to it without lossing our changes. Prisma doesn't touch comments while doing introspection of the DB, so we can exploit this feature for this plugin system and at the same time behing completly transparent to what does prisma underneath.
//
Lets start with the basics,
Plain prisma comments would be leaved as it is, they would be comments that appear only in schema.prisma
.
From Prisma schema documentation:
// comment: This comment is for the reader's clarity and is not present in the abstract syntax tree (AST) of the schema file.
schema.prisma
// This is a basic plain comment
///
From Prisma schema documentation:
/// comment: These comments will show up in the abstract syntax tree (AST) of the schema file, either as descriptions to AST nodes or as free-floating comments. Tools can then use these comments to provide additional information.
Example of posible usage an expected output:
schema.prisma
/// This comment will appear in the generated code at the model class
model user {
/// This comment will appear in the generated code at the 'id' field
id Int @id @default(autoincrement())
}
@generated/models/User.ts
import * as TypeGraphQL from "type-graphql";
/* This comment will appear in the generated code at the model class */
@TypeGraphQL.ObjectType({
description: "This comment will appear in the generated code",
})
export class User {
/* This comment will appear in the generated code at the 'id' field */
@TypeGraphQL.Field(_type => TypeGraphQL.Int, {
nullable: false,
description: "This comment will appear in the generated code at the 'id' field",
})
id!: number;
}
For future posible compatiblity with Prisma as a plugin system with first-class citizen support, we could use the same concepts used by Prisma Attributes with some minor changes to work with the limitations of having to start them with an AST comment.
If something is not overriden by this spec, its expected to be implemented following the default Prisma attributes spec.
Plugins need to register a namespace for them to use, so all AST comment attributes would need to follow a schema like this:
Examples:
/// @Namespace.method
/// @TypeGraphQL.field(name: "firstName")
/// @Validator.IsEmail
/// @Validator.MaxLength(30)
/// @Validator.Min(value: 18, message: "You must be atleast $constraint1 years old")
Prisma Attributes defines this as field attributes:
Field attributes are marked by an @ prefix placed at the end of the field definition. You can have as many field attributes as you want and they may also span multiple lines
Given the AST comments limitations, for our case they can appear in top of the field and at the end of the field.
Prisma Attributes defines this as block attributes:
Field attributes are marked by an @@ prefix placed anywhere inside the block. You can have as many block attributes as you want and they may also span multiple lines
Given the AST comments limitations, for our case they can appear only in top of the model block.
Currently typegraph-prisma
uses two ways of extending the default CRUD behaviour, one that its used only internaly by TypeGraphQL that the consumers of this library can't extend currently, and another that its decorator based, but in the current state only lets decorate the resolvers, so its not specific enough to have complete control of the CRUD resources and implement plugins in top of it, works for stuff like Authorized or not for this resource, but doesn't leave much space for let's say this field should be validated and if its not an email stop the user request.
Currently they are used in this features of the library:
Uses the schema.prisma
as the single source of truth to extend the CRUD behaviour, but in the current state this can only be extended by adding glue code inside the library, so if let's say a consumer of the library wants to add class-validator to his CRUD it would need to do a fork of the project and hack his way arround to extend the doc anotations to add stuff like:
model User {
id Int @id @default(autoincrement())
/// @Validator.IsEmail()
email String @unique
/// @Validator.MaxLength(length: 30)
name String?
/// @Validator.Min(value: 18, message: "You must be atleast $constraint1 years old")
age Int
}
Implemented via:
Additional decorators for Prisma schema resolvers
Currently this is the only way for library consumers to extend the CRUD behaviour, in the current state it only lets consumers to add decorators to CRUD resolver methods.
schema.prisma
extension system
Everything would be located in the same config file (schema.prisma
), no need to maintain multiple config files to configure the DB schema + CRUD behaviour, one with prisma + TypeGraphQL and other's with validation, auth etc, that would introduce one of the problems that prisma tries to eliminate at the ORM level, this proposal tries to solve all the burden for the 90% of the code that usually is for the CRUD's sake, and would let the consumer with only a master schema.prisma
to handle that 90% of code for them, and only implement the reimaining 10% for the bussiness specific logic without lossing control over the CRUD bussiness integration, but at the same time the CRUD would not be in the way of the bussiness custom logic.
This would let with a quick look at the schema.prisma
see everything related to the CRUD. And moving all CRUD related behaviour and configuration to the same step would remove the syncronization problem introduced by clasic ORM's (See Drawbacks of ORMs, point 2, by prisma), if multiple config files exist at multiple stages that would introduce the syncronization problem where your schema.prisma
changed, now you have to go to all your configs that work on top of that result to handle the schema changes downstream.
An implementation working on top of schema.prisma
could be also handled at some basic level from within the runtime capabilities of the runtime added decorators if functionality is added to add decorators for fields, but this way the posible plugins for this system would be limited to mostly decorators and would lose customization over the generated CRUD at a code level.
Having that 90% of the code be generated with all the required structure to handle the bussiness integration would open the option to get code coverage to all the CRUD + bussiness rules, this way bussiness problems like wanting to enforce that all the exposed CRUD have Auth guards, or that all fields have some form of validation could be resolved with current code coverage tools in the market.
Let's say you want to add a new decorator to some models, let's imagine that a plugin system working on top of runtime added decorators exists. You add the config options to the required files, start your project and... Its not working, wellcome to debugging madness, you have a config file with your wanted configuration, some code for your decorators but somewhere in the black box there had been a problem. You have a CRUD + TypeGraphQL generated code, a black box that adds your stuff in memory and then your app code, have fun going step by step with the debugger until you catch the problem between the generated code and your code.
Now, lets imagine the other case, you are working in your special decorator, you add the plugin to the list of plugins to be loaded by typegraphql-prisma
, add the corresponding doc lines to schema.prisma
and generate code... The generator finishes, you try your app with the new decorated CRUD, and its not working? Wellcome to debugging land! But now you have complete control over the generated code and even better, you can see exactly what is happening in the generated code, a quick look at the generated code would get a quick answer about why its not working, making your changes to the generated code and then implementing them back to your plugin, because at the end of the day its not more than code for your eyes to see and the plugin is just writing it for you in an automated, predictable way.
This also lets the user with the option to eject from the typegraphql-prisma
generator, if somehow it has a really weird bussiness case where that 90% of code would need to be manually modified at any point the user can stop using the generator, all the code is there for him to be modified as he wish, included the business rules.
for example, this is generated update args
export declare class AccountSecretUpdateWithoutUserInput { id?: IntFieldUpdateOperationsInput | undefined; phone?: NullableStringFieldUpdateOperationsInput | undefined; }
when we use this to update , we must write
{ phone: { set: $phone } }
can we support this in mutation(update):
`
export declare class AccountSecretUpdateWithoutUserInput {
id?: IntFieldUpdateOperationsInput | number | undefined;
phone?: NullableStringFieldUpdateOperationsInput | string | undefined;
}
{ phone: $phone }
`
I've got 2 models with the following structure, both use the same unique field mapping on type and token, but use different enum types. There is only 1 type generated for these compounded fields (or the other is overridden), but it's causing typescript to complain when querying via the compounded unique fields since the types are incorrect.
model Token {
id String @id @default(cuid())
type TokenType
token String
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@unique([type, token])
}
model UserCredential {
id String @id @default(cuid())
type CredentialType
token String @unique
created_at DateTime @default(now())
updated_at DateTime @updatedAt
user User @relation(fields: [user_id], references: [id])
user_id String
@@unique([type, token])
}
Generated Type
export type TypeTokenCompoundUniqueInput = {
type: CredentialType
token: string
}
First off, I love this library and how seamlessly it works with Prisma!
I'm finding myself needing to basically re-implement resolvers that have been generated with npx prisma generate
because I need to add some additional Authorization to them. The TypeGraphQL documentation lists how I could go about adding authorization on custom resolvers, but is there a way to do it for resolers that TypeGraphQL-Prisma generates? Otherwise, I'd need to basically reimplement resolvers for things that I want to restrict access to.
Not an issue but Prisma version 2.12.0 is released with few changes.
The major change I think is Move most types under the Prisma namespace.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.