Create Prisma 2 Mutation on many to many field using Nexus - prisma-graphql

Hello guys i'm using prisma 2 in my node js serve and below is how i defined the schema of my models
model Interest {
id Int #default(autoincrement()) #id
name String
profile Profile[] #relation(references: [id])
createdAt DateTime #default(now())
updatedAt DateTime #default(now())
}
model Profile {
id Int #default(autoincrement()) #id
username String #unique
user User #relation(fields: [userId], references: [id])
interests Interest[] #relation(references: [id])
userId Int
dob String
location String
skills String
profession String
createdAt DateTime #default(now())
updatedAt DateTime #default(now())
}
So i want to connect profile to interest in my mutation resolver, but i'm not sure how to connect many to many field using prisma 2
this how i approached it, but i'm stuck
return ctx.prisma.profile.create({
data: {
dob: dob,
location: location,
profession: profession,
skills: skills,
username: username,
user: {
connect: {
id: Number(userId),
},
},
interests: {
create: []
}
},
})

Related

How to use null safety of data model classes with graphql?

Let's assume we have the following graphql schema:
type Query {
getPost(id: ID!): Post!
getUserProfile(id: ID!): User!
}
type User {
id: ID!
name: String!
gender: String!
dob: Date!
}
type Post {
id: ID!
creator: User!
caption: String!
}
Here are the graphql queries that we are making on frontend:
getPost(id:"pid1") {
id
creator {
id
name
}
}
getUserProfile(id: "uid1") {
id
name
gender
dob
}
This is the data model class on the frontend side with which we are deserialising the server response:
class UserModel {
String id;
String name;
String? gender; // ? means nullable
String? dob; // ? means nullable
}
My issue is that we have to mark the gender & dob fields as nullable because we aren't fetching them in the getPost query hence absence of them in the server response will make them null. However, gender and dob are always non-nullable in the backend as well as in the GQL API contract defined above. So we cannot use the null-safety feature of the client-side language for these fields and are forced to check if there are null or not. Is there a workaround or better way to treat such fields as null safe?

Prisma 2 delete mutation is returning null

I am using ApolloServer/Prisma2/GraphQL/Typescript/MySQL
I have created two models, User, and Post. My createUser and createPost mutations are working fine. However, I am having trouble getting my delete mutations working. Focusing on the deletePost, here is what I have in my code.
<<schema.graphql>>
type Query {
posts: [Post!]!
users: [User!]!
}
type Mutation {
createPost(title: String!, body: String!): Post!
createUser(name: String!, email: String!, password: String!): User!
deletePost(postId: ID!): Post
deleteAllPosts: [Post!]!
}
type Post {
id: ID!
title: String!
body: String!
author: User
published: Boolean!
}
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
<<schema.prisma>>
model Post {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt #default(now())
title String
body String
published Boolean #default(false)
postedBy User? #relation(fields: [authorId], references: [id])
authorId Int?
}
model User {
id Int #id #default(autoincrement())
name String
email String #unique
password String
posts Post[]
}
<<Mutation.ts>>
async function deletePost(parent, { postId }, context, info) {
return await context.prisma.post.delete(
{
where {
id: parseInt(postId)
}
},
info
)
}
Note that the createPost and createUser are also in the Mutation.ts file and are working correctly. So I'm assuming there is no issue with the Apollo server.
When I use the GraphQL playground I use the following:
mutation {
deletePost(postId: "1") {
id
}
}
with the following result:
{
"data": {
"deletePost": null
}
}
I want the mutation to return the deleted post (at least the id if nothing else). In addition, the database is not deleting anything. I'd appreciate any help.

Apollo and Prisma Graphql explicit model relationship not queryable

I have begun testing out prisma 2 and graphql in general for a new application. I am running into an issue with an explicit many to many table on being able to query relations.
Here is my apollo schema:
scalar DateTime
type Query {
user(id: String!): User
users: [User]
spaces: [Space]
roles: [Role]
}
type Mutation {
createUser(id: String!, email: String!): User!
createSpace(name: String!): Space!
}
type User {
id: ID!
email: String!
spaces: [UserSpace!]
createdAt: DateTime!
updatedAt: DateTime!
}
type Space {
id: ID!
name: String!
users: [UserSpace!]
createdAt: DateTime!
updatedAt: DateTime!
}
type Role {
id: ID!
name: String!
description: String!
users: UserSpace
createdAt: DateTime!
updatedAt: DateTime!
}
type UserSpace {
id: ID!
user: User!
space: Space!
role: Role!
createdAt: DateTime!
updatedAt: DateTime!
}
Here is my prisma schema:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// npx prisma migrate dev
// npx prisma generate
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String #id
email String #unique
spaces UserSpace[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model Space {
id Int #default(autoincrement()) #id
name String #unique
users UserSpace[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model Role {
id Int #default(autoincrement()) #id
name String #unique
description String
users UserSpace[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model UserSpace {
id Int #default(autoincrement()) #id
user User #relation(fields: [userId], references: [id])
userId String
space Space #relation(fields: [spaceId], references: [id])
spaceId Int
role Role #relation(fields: [roleId], references: [id])
roleId Int
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
Here is my mutations resolver:
const { prisma } = require(".prisma/client");
async function createUser(parent, args, context, info) {
return await context.prisma.user.create({
data: {
...args,
},
});
}
async function createSpace(parent, args, context, info) {
const isAuthenticated = context.authentication.isAuthenticated;
let role = null;
if(!isAuthenticated) {
throw new Error("Not Authenticated");
}
try {
role = await context.prisma.role.findUnique({
where: {
name: "Space Administrator",
},
});
}
catch(err) {
throw new Error(err);
}
return await context.prisma.space.create({
data: {
...args,
users: {
create: {
role: {
connect: { id: role.id },
},
user: {
connect: { id: context.authentication.auth0Id },
},
},
},
},
});
}
module.exports = {
createUser,
createSpace,
}
Here is my user resolver (I know this is where the problem is however I do not know how to solve the issue):
function spaces(parent, args, context, info) {
return context.prisma.user.findUnique({ where: { id: parent.id } }).spaces();
}
module.exports = {
spaces,
}
Basically when I create the space the user is added as a Space Administrator to the space and then should be able to be queried with the following:
query {
users {
id
email
spaces {
id
role {
name
}
space {
name
}
}
createdAt
}
}
However when I run the query I get the following error:
"message": "Cannot return null for non-nullable field UserSpace.role.",
How in prisma 2 do I make the resolver for the users work with an explicit many to many table and how it has the third relation in there? I am new to prisma and graphql so if there anything else that stands out also I would like to have the input.
I'm using the word type to refer to object-models in your GraphQL schema and model to refer to data-models in your Prisma Schema.
The Problem
I see that you have a User type resolver, that has a resolver function for User.spaces field in your User type. The query that you have defined in your User.spaces resolver will return the relevant userSpace records from the database.
However, these userSpace records do not by default resolve the role field, as it is a relation field. This is how prisma works (relation fields are not resolved by default, unless explicitly stated).
Solution
Create a resolver for the UserSpace type and explicitly define the the resolver function for UserSpace.role field. This is what it will look like
// UserSpace resolver module
function role(parent, args, context, info) {
return context.prisma.userSpace.findUnique({ where: { id: parent.id } }).role();
}
module.exports = {
role,
}
While there are some other ways to solve this problem, the way I have shown (along with the specific syntax) is recommended because under the hood it allows prisma to perform certain optimizations to solve the n+1 query problem. But, if you don't know what that is, you don't necessarily need to worry about it either.
Did you provide the value to users arg? like this: users(id: "string_value"). Because is id is required.

GraphQL mutation - confusion designing gql tag for Apollo Client

I need help figuring out the GraphQL tag for use with Apollo Client. The Docs don't go far beyond the most basic use case for mutations.
My goal is to have the only required input be an email. If the other variables are present, I would like those to be accepted and create a proposal with all that information.
I have the mutation (in both only email and full variables scenarios) working successfully on the GraphQl Playground (if it helps, you can find it here and test it out, look at the schema, etc.,): https://prisma2-graphql-yoga-shield.now.sh/playground)
mutation {
createOneProposal(
data: {
email: "fake#gmail.com"
name: "Sean"
types: {
create: {
model: PURCHASE
name: "e-commerce"
cost: 600
services: {
create: [
{ service: "Responsive web design" }
{ service: "Another service!" }
{ service: "And yet another service!" }
]
}
}
}
}
) {
created_at
proposal_id
types {
cost
model
name
type_id
services {
service
service_id
}
}
}
}
Producing as a result:
{
"data": {
"createOneProposal": {
"created_at": "2020-02-27T21:28:53.256Z",
"proposal_id": 35,
"types": [
{
"cost": 600,
"model": "PURCHASE",
"name": "e-commerce",
"type_id": 6,
"services": [
{
"service": "Responsive web design",
"service_id": 10
},
{
"service": "Another service!",
"service_id": 11
},
{
"service": "And yet another service!",
"service_id": 12
}
]
}
]
}
}
}
My initial design for the gql tag:
export const NEW_PROPOSAL = gql`
mutation createOneProposal(
$email: String!
$name: String
$cost: Int
$model: Model
$service: Service
) {
createOneProposal(
email: $email
name: $name
cost: $cost
model: $model
service: $service
) {
created_at
proposal_id
types {
services {
service_id
}
}
}
}
`;
But, I get a lot of errors with this.
{"errors":[
{"Variable "$service" cannot be non-input type `"Service`".","locations":[{"line":1,"column":97}]},
{"Unknown argument "email" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":21}]},
{"Unknown argument "name" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":36}]},
{"Unknown argument"cost" on field "createOneProposal\" of type "Mutation`".","locations":[{"line":2,"column":49}]},
{"Unknown argument "model" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":62}]},
{"Unknown argument "service" on field "createOneProposal`" of type "Mutation`".","locations":[{"line":2,"column":77}]},
{"Field "createOneProposal" argument "data" of type "ProposalCreateInput!`" is required, but it was not provided.","locations":[{"line":2,"column":3}]}]}
So, how can I go about this... I figured out the query version (much easier...), but I just can't figure this out!
My schema, if it helps:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("MYSQL_URL_PRISMA2")
}
model Post {
content String #default("")
created_at DateTime #default(now())
post_id Int #default(autoincrement()) #id
published Boolean #default(false)
published_at DateTime?
title String #default("")
author User
}
model Profile {
bio String?
profile_id Int #default(autoincrement()) #id
user_id User
}
model Proposal {
email String #unique
name String?
proposal_id Int #default(autoincrement()) #id
created_at DateTime #default(now())
types Type[]
}
model Type {
cost Int?
name String?
model Model? #default(SUBSCRIPTION)
services Service[]
type_id Int #default(autoincrement()) #id
proposal_id Proposal
}
model Service {
service_id Int #default(autoincrement()) #id
service String?
type_id Type
}
model User {
email String #default("") #unique
name String #default("")
password String #default("")
role Role #default(USER)
user_id Int #default(autoincrement()) #id
posts Post[]
profiles Profile[]
}
enum Role {
USER ADMIN
}
enum Model {
SUBSCRIPTION PURCHASE CUSTOM
}
GraphQL types are categorized as either input types or output types. Input types are used for inputs like variable definitions or argument definitions. Output types are used for typing fields, which are what compose the actual response. Certain types, like scalars and enums, can be used as either an input or an output. However, with objects, there are output object types (sometimes referred to just object types or objects) and input object types.
Service is an output type, so it can't be used where an input type is expected (in this case, a variable definition). Examine the schema generated by Prisma to determine the appropriate type to use.
Thanks to some very needed direction from #xadm, I figured out the structure of the tag! For anyone who comes across this in the future:
mutation createOneProposal($input: ProposalCreateInput!){
createOneProposal(data:$input){
created_at
name
email
proposal_id
type{
cost
description
model
name
type_id
services{
service
cost
service_id
}
}
}
}

join 3 tables using graphql query

I want to join 3 tables just like we do in mysql based on primary and foreign keys.
Can I do such using graphql(http://graphql.org/)
My table structure along with graphql query is below. Thanks
query($companyId:String){
Data{
reach{
department {
departmentId
departmentName
description
}
companyDepartment(companyId:$companyId) {
primaryId
departmentId
companyId
createdDate
modifiedDate
modifiedBy
}
company(companyId:$companyId) {
companyId
companyName
}
}
}
}
You must break your mind and think in Graph way model :)
Type Company(node) <- CompanyDepartmentConection (name of connection edge) -> Type Department(node)
based on this very useful article, anyway i do for you eg. Schema
Concept
interface Node {
id: ID!
name: String
}
type Company implements Node {
id: ID!
name: String
departmentsConnection: CompanyDepartmentConnection
}
type CompanyDepartmentConnection {
pageInfo: PageInfo!
edges: [CompanyDepartmentEdge]
}
type CompanyDepartmentEdge {
cursor: String!
node: Company
linkedAt: DateTime
}

Resources