Why NestJs saves model with 'null' relation, if it is required in Entity and DTO? - validation

I have an Entities:
Company:
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
#Entity()
export class Company {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
}
Unit:
import { Company } from '../../companies/entities/company.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
#Entity()
export class Unit {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
address: string;
#ManyToOne((type) => Company)
company: Company;
}
And DTOs for them:
export class CompanyCreateDto {
name: string
}
export class UnitCreateDto {
name: string
address: string
company: { id: number }
}
This request works fine, and creates a relation.
POST http://localhost:3000/api/units
content-type: application/json
{
"name": "Unit name 12",
"address": "city, street, house, office 234",
"company": {"id": 1}
}
If I remove "name" or "address", I get an error (which is fine):
ERROR [ExceptionsHandler] null value in column "name" of relation "unit" violates not-null constraint
But if I remove "company" from request, the entity will be saved with company: null.
I can't understand why is that?

Related

Graphql insert a child while indicating his parent

I'm very new into graphql but I'm able to insert a new "notification" that belongs to a "Tienda" which means store, however I'm not able to indicate to which store it belongs to.
This is the schema
type Tienda #model {
id: ID!
name: String!
cliente: [Cliente] #manyToMany(relationName: "clienteDeTienda")
news: [Notificacion] #hasMany
}
type Cliente #model {
id: ID!
name: String!
stores: [Tienda] #manyToMany(relationName: "clienteDeTienda")
}
type Notificacion #model {
id: ID!
store: Tienda #belongsTo
content: String!
}
This is how I insert a new notification:
const response = await API.graphql(graphqlOperation(createNotificacion, {input:data}))
This is what data has inside:
Object {
"content": "Nueva notificacion",
}
And this is the response I get from console.log(response):
Object {
"data": Object {
"createNotificacion": Object {
"content": "Nueva notificacion",
"createdAt": "2022-11-13T18:04:55.000Z",
"id": "9f50d333-fc9e-478f-be6a-c54275797a27",
"store": null,
"tiendaNewsId": null,
"updatedAt": "2022-11-13T18:04:55.000Z",
},
},
}
As you can see "content" was added to the notification but "store" is null but I haven't been successful in inserting the store ID in there.
This is how the mutations is defined:
export const createNotificacion = /* GraphQL */ `
mutation CreateNotificacion(
$input: CreateNotificacionInput!
$condition: ModelNotificacionConditionInput
) {
createNotificacion(input: $input, condition: $condition) {
id
store {
id
name
cliente {
nextToken
}
news {
nextToken
}
createdAt
updatedAt
}
content
createdAt
updatedAt
tiendaNewsId
}
}
`;
I've tried with the following 3 methods which give no error but still doesn't link to it's parent:
API.graphql(graphqlOperation(createNotificacion, {input:data, store:{id:'55988776-11af-42b8-b93b-9de9af35f7dc'}}))
And
API.graphql(graphqlOperation(createNotificacion, {input:data, store:'55988776-11af-42b8-b93b-9de9af35f7dc'}))
And:
API.graphql(graphqlOperation(createNotificacion, {input:data},{store:'55988776-11af-42b8-b93b-9de9af35f7dc'}))
Please somebody point me the right path or documentation about how to do this. I need to know how to perform the graphqlOperation correctly.
Thank you very much
In the end what I had to do is this:
const response = await API.graphql(graphqlOperation(createNotification, {input:{...data, storeNotificationsId:'someValidStoreId'}}));

Getting error with GraphQL model in NestJS, using code-first approach

I'm using the code-first approach in NestJS to create GraphQL schemas. I have the following definition. Person can have a Dog.
#ObjectType()
export class Person {
#Field()
name: string;
#Field(type => Dog, { nullable: true })
dog?: Dog;
}
#ObjectType()
export class Dog {
#Field()
breed: string
#Field()
name: string
}
When server starts up I get the following error message.
ReferenceError: Cannot access 'Dog' before initialization
What am I doing wrong? Does Dog need to be resolved by #ResolveField() in the resolver? In my case it's just a nested object within Person. Dog doesn't come from another service call.
Essentially, I want to represent the following structure.
interface Person {
name: string;
dog?: {
breed: string;
name: string
}
}
I think all you need to do is declare Dog first:
#ObjectType()
export class Dog {
#Field()
breed: string;
#Field()
name: string;
}
#ObjectType()
export class Person {
#Field()
name: string;
#Field(type => Dog, { nullable: true })
dog?: Dog;
}

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
}
}
}
}

Can't extending a remote graphql nested input type

graphql service 1 type defs:
import { gql } from 'apollo-server';
const typeDefs = gql`
type Post {
postId: ID!
postTitle: String!
postContent: String!
postAuthorId: ID
}
input PostTag {
name: String!
}
input PostInput {
postTitle: String!
postContent: String!
postAuthorId: ID!
postTags: [PostTag!]!
}
type CommonResponse {
code: Int!
message: String!
}
type Query {
posts: [Post]!
}
type Mutation {
addPost(post: PostInput): CommonResponse!
}
`;
export { typeDefs };
Now, graphql service 2 wants to extend PostTag input type from graphql service 1 like this:
import { gql } from 'apollo-server';
const typeDefs = gql`
extend input PostTag {
color: String
}
`;
export { typeDefs };
I print stitching schema, it's correct.
enum CacheControlScope {
PUBLIC
PRIVATE
}
type CommonResponse {
code: Int!
message: String!
}
type Mutation {
addPost(post: PostInput): CommonResponse!
}
type Post {
postId: ID!
postTitle: String!
postContent: String!
postAuthorId: ID
}
input PostInput {
postTitle: String!
postContent: String!
postAuthorId: ID!
postTags: [PostTag!]!
}
input PostTag {
name: String!
color: String
}
type Query {
posts: [Post]!
}
"""The `Upload` scalar type represents a file upload."""
scalar Upload
But when client sends a mutation like this:
mutation{
addPost(post: {
postTitle: "ez2on",
postContent: "golang",
postAuthorId: "1",
postTags: [{
name: "222",
color: "red"
}]
}){
code
message
}
}
Got this error:
{
"errors": [
{
"message": "Variable \"$_v0_post\" got invalid value { postTitle: \"ez2on\", postContent: \"golang\", postAuthorId: \"1\", postTags: [[Object]] }; Field \"color\" is not defined by type PostTag at value.postTags[0].",
"locations": [
{
"line": 7,
"column": 3
}
],
"path": [
"addPost"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"errors": [
{
"message": "Variable \"$_v0_post\" got invalid value { postTitle: \"ez2on\", postContent: \"golang\", postAuthorId: \"1\", postTags: [[Object]] }; Field \"color\" is not defined by type PostTag at value.postTags[0].",
"locations": []
}
],
"stacktrace": [
"Error: Variable \"$_v0_post\" got invalid value { postTitle: \"ez2on\", postContent: \"golang\", postAuthorId: \"1\", postTags: [[Object]] }; Field \"color\" is not defined by type PostTag at value.postTags[0].",
" at new CombinedError (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/stitching/errors.ts:90:5)",
" at Object.checkResultAndHandleErrors (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/stitching/errors.ts:111:11)",
" at CheckResultAndHandleErrors.transformResult (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/transforms/CheckResultAndHandleErrors.ts:15:12)",
" at /Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/transforms/transforms.ts:37:45",
" at Array.reduce (<anonymous>)",
" at applyResultTransforms (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/transforms/transforms.ts:35:21)",
" at /Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/src/stitching/delegateToSchema.ts:104:12",
" at step (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/dist/stitching/delegateToSchema.js:31:23)",
" at Object.next (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/dist/stitching/delegateToSchema.js:12:53)",
" at fulfilled (/Users/ldu020/workspace/github.com/mrdulin/apollo-graphql-tutorial/node_modules/graphql-tools/dist/stitching/delegateToSchema.js:3:58)"
]
}
}
}
],
"data": null
}
It's important to keep in mind that makeRemoteExecutableSchema just "uses the [provided] link to delegate requests to the underlying service". When you query fields from the remote schema, it's delegating the request for those particular fields to the remote server, effectively rerouting the request. This is true whether you stitch the schema with some other one, or use it by itself.
Schema stitching allows you to combine any number of local and remote schemas. However, any remote schemas will still have their fields resolved by their respective servers.
Because stitching merges the provided schemas' type definitions, you can use the extend keyword inside one schema to modify types from another, even if it's a remote schema. If we extend an object type, we can also add some resolvers to help resolve the fields we've added.
Extending a remote schema's input object is a bit different. There's no "resolving" input objects. Instead, all we do by extending it is saying "these fields are also valid". However, when we request some remote schema field that takes this modified input object as an argument, the resolution of this field is, again, delegated to the underlying remote schema. It gets the modified input object and when it validates it, it finds extra fields and throws an error.
In other words, it's not possible to extend input types like this. And consider, even if the request didn't fail validation -- even if you extend the input type, the original resolver has not been changed and so it necessarily won't know how to handle the additional input type fields anyway.
NOTE: If you do the above but with two local schemas, the extension should work as expected because there is no delegation in this case. You're still left with a resolver that doesn't necessarily know how to handle the new input object field though.

Graphql with mutation spring boot

My schema file is
type Mutation {
createCustomer(name: String!, email: String!, product: [Product]): Customer
}
input Product {
id: ID!
name: String!
price: Int
}
interface Person {
id: ID!
name: String!
email: String!
}
type Customer implements Person {
id: ID!
name: String!
email: String!
product: [Product]
}
I want to insert customer detail here which has product list as input. My query is
mutation {
createCustomer(
name: "kitte",
email: "kitte#gmail.com",
product: [
{
name: "soap",
price: 435,
}
]
)
{
id
name
email
product{name}
}
}
But I am getting exception
{
"data": null,
"errors": [
{
"validationErrorType": "WrongType",
"message": "Validation error of type WrongType: argument value ArrayValue{values=[ObjectValue{objectFields=[ObjectField{name='name', value=StringValue{value='dars76788hi'}}, ObjectField{name='price', value=IntValue{value=123}}]}, ObjectValue{objectFields=[ObjectField{name='name', value=StringValue{value='darr'}}, ObjectField{name='price', value=IntValue{value=145}}]}]} has wrong type",
"locations": [
{
"line": 5,
"column": 5
}
],
"errorType": "ValidationError"
}
]
}
I don't understand what is the error. And how to pass list to mutation. I have referred some examples but not able to insert product as list.
Make sure you are passing the right type of objects to your mutation. GraphQL needs separate types for input fields. In your schema, Product types should be something like this and you should change the mutation accordingly.
type Product {
id: ID!
name: String!
price: Int
}
input ProductInput {
name: String!
price: Int
}
input CustomerInput {
...
products: [ProductInput]
}
There are couple of very useful examples in the docs, see Mutations and Input Types

Resources