GithubHelp home page GithubHelp logo

Comments (9)

dalssoft avatar dalssoft commented on August 29, 2024

The current implementation already create the CustomerSubscription but not the CustomerSubscriptionInput, right?

What if we remove the Input at the end of the name, would it work?

from herbs2gql.

italojs avatar italojs commented on August 29, 2024

to receive complex objects into mutation as params we need to create a Input instead of a type https://graphql.org/graphql-js/mutations-and-input-types/ So as I know, we need the input

from herbs2gql.

dalssoft avatar dalssoft commented on August 29, 2024

to receive complex objects into mutation as params we need to create a Input instead of a type https://graphql.org/graphql-js/mutations-and-input-types/ So as I know, we need the input

TL;DR: I don't think we should use Inputs with Herbs at all. (1) It won't work well with the way we extract the request object from the use case. (2) I don't see the benefit of using Inputs at all.

Complete answer:

As I understand, we don't really need Inputs, like a MUST have. It's more like an Apollo-imposed (and debatable) best practice. More on that later.

However, I don't think it will work well with the way use create the metadata for Herbs use cases.

On the doc you informed they recommend to create a Input for a Message object so it can be used to create and update mutations, where on the creation scenario there is no ID:

input MessageInput {
  content: String
  author: String
}

type Message {
  id: ID!
  content: String
  author: String
}

type Query {
  getMessage(id: ID!): Message
}

type Mutation {
  createMessage(input: MessageInput): Message
  updateMessage(id: ID!, input: MessageInput): Message
}

The only difference from Message type and MessageInput input is that MessageInput doesn't have ID.

With Herbs, the way we declare the request of a use case is different.

usecase('Create Message', {
    request: request.from(Message, { ignoreIDs: true }), // { content: String, author: String }
...

usecase('Delete Message', {
    request: { id: String },
...

    
usecase('Find a Message', {
    request: { id: String },
...

usecase('Update Message', {
    request: request.from(Message), // { id: String, content: String, author: String }

So it won't be possible to generate a Input from the request object given that resquest.from extract the fields and the information about the original entity (Message) is lost.

All of that to say that I don't think we should use Inputs with Herbs at all.

We could use scalar types for simple objects:

type Mutation {
  createMessage(content: String, author: String): Message
  updateMessage(id: ID!, content: String, author: String): Message
}

This is very straight forward for Herbs to work with.

And use gql types for entity objects:

entity('Message', {
    id: String,
    content: String,
    user: User // entity object
})

usecase('Create Message', {
    request: request.from(Message, { ignoreIDs: true }), // { content: String, user: User }
...
type Message {
  id: ID!
  content: String
  user: User
}

type User {
  id: ID!
  name: String
  ...
}

type Mutation {
  createMessage(content: String, user: User): Message
  updateMessage(id: ID!, content: String, user: User): Message
}

This translates well to the way Herbs works. Actully, the first implementation of Herbs GraphQL was like that. But we changed to use Inputs because it was the recommended way by Apollo, but it seems that it doesn't work well.

Also, regardless of Herbs, I have a hard time to see the benefit of using Inputs.

Why this:

input MessageInput {
  content: String
  author: String
}

type Mutation {
  createMessage(input: MessageInput): Message
  updateMessage(id: ID!, input: MessageInput): Message
}

is better than this?

type Mutation {
  createMessage(content: String, author: String): Message
  updateMessage(id: ID!, content: String, author: String): Message
}

Given than in both cases we need to inform content and author to create a message. I doubt that having Input will make the life of the frontend developer easier. It seems to be a bad abstraction to me.

from herbs2gql.

italojs avatar italojs commented on August 29, 2024

how do you see the DX for large and pretty complex objects? like

{
  "endereço": {
    "rua": "Avenida Principal",
    "número": 123,
    "complemento": "Bloco A",
    "bairro": "Centro",
    "cidade": "Exemploville",
    "estado": "Exemplostate",
    "país": "Exempleland",
    "cep": "12345-678",
    "coordenadas": {
      "latitude": 40.712776,
      "longitude": -74.005974
    },
    "informações_adicionais": {
      "tipo": "Residencial",
      "ano_construção": 2005,
      "proprietário": {
        "nome": "João da Silva",
        "idade": 35,
        "contato": {
          "telefone": "(00) 1234-5678",
          "email": "[email protected]"
        }
      },
      "areas": {
        "quartos": 3,
        "banheiros": 2,
        "sala_estar": true,
        "sala_jantar": true,
        "cozinha": {
          "tipo": "Americana",
          "eletrodomésticos": ["geladeira", "fogão", "microondas"]
        }
      }
    }
  }
}

My point is, in a scenario like this we will handle everything as scalar? the DX experience sont will be interesting

another thing is, the difference from the request object could be totally different from the final entity, i think you are looking for simple scenarios but the framework will be used in many scenarios that we cant imagine, so base us on request.from to build the Inputs is super important

from herbs2gql.

dalssoft avatar dalssoft commented on August 29, 2024

For coordenadas, informações_adicionais, proprietário, contato, areas I guess it would be necessary an entity (Herbs side) and a type (GQL side). If we use Inputs we would have to create a Input for each of those objects. Again, I don't see the benefit of using Inputs here anyway. Please, enlighten me if I'm missing something.

BTW, "Input types can't have fields that are other objects, only basic scalar types, list types, and other input types." here

For the rest, like endereço, rua, número, etc it would be scalar types as there is no other way to represent them.

the difference from the request object could be totally different from the final entity

request.from is just a helper to extract the fields from the entity. If the request is different from the entity, you can just create the request object manually and it should work fine.

from herbs2gql.

italojs avatar italojs commented on August 29, 2024

I will do a check if grahphql accepts type as mutation param, i didnt find it on internet saying it accepts, but didnt find saying it not accept too

from herbs2gql.

dalssoft avatar dalssoft commented on August 29, 2024

I found a stackoverflow answer that tries to argument in favor of inputs:
https://stackoverflow.com/questions/41743253/whats-the-point-of-input-type-in-graphql

The main arguments are that Inputs are different than objects used to output. I could agree with that if Herbs2GQL was using all the features for validation and default value.

However I don't see these arguments strong enough since Herbs2GQL generates only simple objects (types and accepting nulls) leaving complex validations and defaults for the entities and use cases. So, it is ok to have just one abstraction for input and output, just like Herbs just have one kind of entity.

I don't see Herbs2GQL generating complex objects and using all the capabilities of GraphQL in the future.

from herbs2gql.

italojs avatar italojs commented on August 29, 2024

i don't know how to use type as mutation params in graphql, I tried it but i always got errors like
The type of Mutation.updatePrice(product:) must be Input Type but got: Product.

i remember I got this error some year ago and because of this we implemented using Inputs

my gqlConverter.js

const { checker } = require('@herbsjs/suma')
const { camelCase, upperFirst } = require('lodash')
const { BaseEntity } = require('@herbsjs/gotu/src/baseEntity')
const stringCase = require('./stringCase')

function requestFieldType2gql(type, presence, input) {
    let name
    if (Array.isArray(type))
        name = `[${requestFieldType2gql(type[0], false, input)}]`
    else if (type === Number)
        name = `Float`
    else if (type.prototype instanceof BaseEntity) 
        name = `${upperFirst(camelCase(type.name))}`
        // name = `${upperFirst(camelCase(type.name))}${input ? 'Input' : ''}`
    else
        name = stringCase.pascalCase(type.name)

    return presence ? `${name}!` : name
}

function usecaseRequest2gql(useCase, presence) {
    const fields = Object.keys(useCase.requestSchema)
    const output = []
    for (const field of fields) {
        const type = useCase.requestSchema[field]
        let name = requestFieldType2gql(type, presence, true)
        output.push(`${field}: ${name}`)
    }
    return output.join(`, `)
}

function usecaseResponse2gql(useCase, presence) {
    let name = requestFieldType2gql(useCase.responseSchema, presence, false)
    return name
}

function schemaOptions(options) {
    return Object.assign({
        presenceOnRequest: false,
        presenceOnResponse: false
    }, options || {})
}

function entityFieldType2gql(type, param) {
    let name
    if (Array.isArray(type)) name = `[${entityFieldType2gql(type[0], param)}]`
    else if (type === Number) name = `Float`
    else if (type.prototype instanceof BaseEntity) {
        name = upperFirst(camelCase(type.name))
        // if(param == 'input') name = `${upperFirst(camelCase(type.name))}Input`
    }
    else name = type.name
    return name
}

function entityField2gql(entity, param) {
    const fields = Object.keys(entity.prototype.meta.schema)    
    let gql = ""
    for (const field of fields) {
        
        if(typeof entity.prototype.meta.schema[field] === 'function') continue        

        const { type, options } = entity.prototype.meta.schema[field]

        let name = entityFieldType2gql(type, param)

        let typeOptions = fieldOptions2gpq(options)

        gql += `${field}: ${name}${typeOptions}\n`
    }
    return gql
}

function fieldOptions2gpq(options) {
    let optionsGql = ``
    const { validation } = options

    if (validation) validation.presence && (optionsGql += `!`)

    return optionsGql
}

function usecaseFieldToParams(useCase, schema) {
    const hasResponse = !checker.isEmpty(useCase.requestSchema)
    if (hasResponse) return `(${usecaseRequest2gql(useCase, schema.presenceOnRequest)}) `
    return ''
}

module.exports = {
    requestFieldType2gql,
    usecaseRequest2gql,
    usecaseResponse2gql,
    usecaseFieldToParams,
    schemaOptions,
    entityFieldType2gql,
    entityField2gql
}

from herbs2gql.

dalssoft avatar dalssoft commented on August 29, 2024

Solved in beta. Planned for the next release

from herbs2gql.

Related Issues (12)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.