You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang+Gorm+GraphQL创建用户报错invalid input问题排查

问题:Golang+Gorm+PostgreSQL开发GraphQL服务,createuser mutation返回"invalid input"错误

使用Golang结合Gorm、PostgreSQL开发GraphQL服务,调用http://localhost:8080/graphql执行createuser mutation时无法创建用户,返回"invalid input"错误。以下是相关代码、请求示例及返回结果:

完整代码片段

基础定义与User模型

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/graphql-go/graphql"
    "github.com/graphql-go/handler"
    "github.com/kaleabbyh/foodrecipie/config"
    _ "github.com/lib/pq"
)

// Note: 表users已通过独立迁移代码成功创建,包含User模型属性
type User struct {
    ID        int       `gorm:"type:int;primary_key;identity(100,1)"`
    Name      string    `gorm:"type:varchar(255);not null"`
    Email     string    `gorm:"uniqueIndex;not null"`
    Password  string    `gorm:"not null"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

func main() {
    config, err := Connection.LoadConfig(".")
    if err != nil {
        log.Fatal("? Could not load environment variables", err)
    }
    Connection.ConnectDB(&config)
    db:=Connection.DB

    userType := graphql.NewObject(graphql.ObjectConfig{
        Name:        "User",
        Description: "An user",
        Fields: graphql.Fields{
            "name": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if user, ok := p.Source.(*User); ok {
                        return user.Name, nil
                    }
                    return nil, nil
                },
            },
            "email": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if user, ok := p.Source.(*User); ok {
                        return user.Email, nil
                    }
                    return nil, nil
                },
            },
            "password": &graphql.Field{
                Type:        graphql.NewNonNull(graphql.String),
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    if user, ok := p.Source.(*User); ok {
                        return user.Email, nil
                    }
                    return nil, nil
                },
            },
        },
    })

Root Mutation定义

rootMutation := graphql.NewObject(graphql.ObjectConfig{
    Name: "RootMutation",
    Fields: graphql.Fields{
        "createuser":&graphql.Field{
            Type:        userType,
            Description: "Create new user",
            Args: graphql.FieldConfigArgument{
                "name": &graphql.ArgumentConfig{
                    Type: graphql.NewNonNull(graphql.String),
                },
                "email": &graphql.ArgumentConfig{
                    Type: graphql.NewNonNull(graphql.String),
                },
                "password": &graphql.ArgumentConfig{
                    Type: graphql.NewNonNull(graphql.String),
                },
            },
            Resolve:func(params graphql.ResolveParams) (interface{}, error) { 
                input, ok := params.Args["input"].(map[string]interface{})
                fmt.Println(input)
                if !ok {
                    return nil, fmt.Errorf("invalid input")
                }
            
                name, _ := input["name"].(string)
                email, _ := input["email"].(string)
                password, _ := input["password"].(string)
            
                user := User{
                        Name:name,
                        Email :email,
                        Password:password,
                    }
                err := db.Create(&user).Error
                if err != nil {
                    return nil, err
                }
            
                return user, nil
            },
        },
    },
})

Root Query与Schema定义

rootQuery := graphql.NewObject(graphql.ObjectConfig{
    Name: "RootQuery",
    Fields: graphql.Fields{
        "users": &graphql.Field{
            Type:        graphql.NewList(userType),
            Description: "List of users.",
            Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                var users []User
                err := db.Find(&users).Error
                checkErr(err)
                fmt.Println(users)
                return users, nil
            },
        },
    },
}) 

schema, _ := graphql.NewSchema(graphql.SchemaConfig{
    Query:    rootQuery,
    Mutation: rootMutation,
})

h := handler.New(&handler.Config{
    Schema:   &schema,
    Pretty:   true,
    GraphiQL: true,
})

// 启动HTTP服务
http.Handle("/graphql", h)
fmt.Println("server is running on port: 8080")
http.ListenAndServe(":8080", nil)
}

请求示例

mutation {
    createuser(
          name: "kaleab", 
          email: "user@example.com", 
          password: "random") {
        name
       } 
}

返回结果

{
      "data": {
      "createuser": null
    },
    "errors": [
      {
      "message": "invalid input",
      "locations": [
          {
          "line": 2,
          "column": 3
         }
        ],
      "path": [
        "createuser"
        ]
       }
      ]
}

错误原因

  1. 参数获取逻辑不匹配:你的createuser Resolve函数尝试从params.Args["input"]获取参数,但实际在Mutation的Args定义里,你把name、email、password作为了顶级参数,并没有封装在input对象中;同时你的请求也是直接传递顶级参数,导致params.Args["input"]不存在,类型断言失败返回错误。
  2. 字段Resolve错误:userType中的password字段Resolve函数错误返回了user.Email,应该返回user.Password。

修复方案

方案1:直接获取顶级参数(匹配当前请求格式)

修改createuser的Resolve函数,直接从params.Args中读取顶级参数:

Resolve: func(params graphql.ResolveParams) (interface{}, error) {
    name, okName := params.Args["name"].(string)
    email, okEmail := params.Args["email"].(string)
    password, okPwd := params.Args["password"].(string)

    // 校验参数类型合法性
    if !okName || !okEmail || !okPwd {
        return nil, fmt.Errorf("invalid input")
    }

    user := User{
        Name:     name,
        Email:    email,
        Password: password,
    }
    err := db.Create(&user).Error
    if err != nil {
        return nil, err
    }

    return user, nil
},

方案2:改用Input对象(更规范的GraphQL实践)

如果想使用input参数的方式,需要先定义Input类型,再调整Mutation和请求格式:

  1. 定义UserInput类型
userInputType := graphql.NewInputObject(graphql.InputObjectConfig{
    Name: "UserInput",
    Fields: graphql.InputObjectConfigFieldMap{
        "name": &graphql.InputObjectFieldConfig{
            Type: graphql.NewNonNull(graphql.String),
        },
        "email": &graphql.InputObjectFieldConfig{
            Type: graphql.NewNonNull(graphql.String),
        },
        "password": &graphql.InputObjectFieldConfig{
            Type: graphql.NewNonNull(graphql.String),
        },
    },
})
  1. 修改createuser的Args定义
Args: graphql.FieldConfigArgument{
    "input": &graphql.ArgumentConfig{
        Type: graphql.NewNonNull(userInputType),
    },
},
  1. 调整请求格式
mutation {
    createuser(input: {
          name: "kaleab", 
          email: "user@example.com", 
          password: "random"
    }) {
        name
    } 
}

额外修复:修正password字段的Resolve函数

"password": &graphql.Field{
    Type:        graphql.NewNonNull(graphql.String),
    Resolve: func(p graphql.ResolveParams) (interface{}, error) {
        if user, ok := p.Source.(*User); ok {
            return user.Password, nil // 替换原错误的user.Email
        }
        return nil, nil
    },
},

内容的提问来源于stack exchange,提问作者kaleab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:35:55