Crud API Gin + Gorm + GraphQL 생성 시도

35907 단어 gowebdevgraphql
나는 보통 루비 온 레이스로 백엔드를 개발하기 때문에 골롱에 익숙하지 않다. 그러나 나는 그것을 연구했고 Gin+Gorm+GraphQL로 GraphQL API를 만들었다.
나는 전체 절차를 설명할 것이다.이어서 나는 나의 일의 퇴적점에 대해 토론할 것이다.
저장소입니다.
gin-gorm-gqlgen-sample

내가 사용하는 기술
다음은 제가 이번에 사용한 기술입니다.
Docker
  • 부(포장 매니저)
  • 잣주(WAF)
  • 옴(ORM)
  • gqlgen(도형 라이브러리)

  • 잣술
    두송자주는 바둑 언어의 일종이다.내가 아는 바에 의하면, 그것은 고랑 WAF의github에서 가장 많은 스타를 얻었다고 한다.

    그램
    Gorm은 Go Lang ORM입니다.웹 글에 따르면, 이 인터페이스는 Rails의 Active Record와 유사하다.
    나는 Rails에 익숙하기 때문에 이 라이브러리를 선택했다.

    부사장
    부사장은 포장 매니저다.라이브러리를 설치해야 하는데 Dockerfile을 방해하는 것 같습니다.
    그래서 제가 dep을 썼어요.

    gqlgen
    gqlgen은GraphQL 서버 라이브러리입니다.Golang에는graphql 라이브러리가 있습니다. 예를 들어 graphql-go/graphql.
    하지만 제가 gqlgen을 선택한 것은 패턴을 정의한 후에 코드를 만드는 것을 매우 좋아하기 때문입니다.
    99designs/gqlgen
    gqlgen을 사용하면 다음과 같은 절차에 따라 개발할 수 있습니다.
  • 정의 모드
  • 해상도
  • 에서 검색에 대한 논리를 작성합니다
    이 과정에서, 당신은 논리에 전념할 수 있으며, 무미건조한 코드를 작성할 필요가 없습니다. 왜냐하면 gqlgen은 당신의 모델을 바탕으로 코드를 생성하기 때문입니다.

    전체 프로세스
    파일의 gqlgen 예는 baout Todo List입니다.만약 네가 《 입문 》 을 읽는다면, 너는 매우 쉽게 시작할 것이다.
    하지만, 당신이 Gorm을 사용하거나 CRUD를 개발할 때, 나는 무엇을 해야 한다.그래서 이런 식으로 해석할 거예요. 그래서 이런 식으로 해석할 거예요.

    dep를 사용하여 gqlgen 설치
    "goget"을 통해 gqlgen을 설치할 수 있지만 dep를 사용하면 패키지 스크립트를 준비해서 샘플 파일을 생성해야 합니다.
    스크립트/gqlgen.가다
    package main
    
    import "github.com/99designs/gqlgen/cmd"
    
    func main() {
        cmd.Execute()
    }
    
    하면, 만약, 만약...
    $go run scripts/gqlgen.go init
    
    너는 이렇게 집행할 수 있다.
    스크립트를 작성한 후 gqlgen에 필요한 파일을 생성합니다.
    $go run scripts/gqlgen.go init
    
    Gqlgen에서 다음 파일을 생성합니다.
  • gqlgen.yml・・・ 구성 파일.configure fo에서 생성된 코드를 작성합니다.
  • 보통입니다.가다・・・ 패턴을 정의하고 스크립트를 실행할 때 이 파일을 생성합니다.수동으로 편집할 수 없습니다. 왜냐하면 그것은 생성된 것이기 때문입니다.
  • 모델・・・ 이 파일도 schema에서 생성된 것이다.수동으로 편집할 수 없습니다. 왜냐하면 그것은 생성된 것이기 때문입니다.
  • 분해기.가다・・・ 이 이름 파일이 생성되지 않은 경우 이 파일이 생성됩니다.
  • 서버/서버.가다・・・ 입구점.
  •  
    코드를 생성할 때 다음 명령을 실행하고 패키지를 설치합니다.
    dep ensure
    

    두송자주 입구점 만들기
    만약 네가 두송자주를 쓰고 싶다면, 나는 두송자주를 위해 입구를 준비해야 한다.
    두 끝점을 준비합니다.(놀이공원과 메인 도로에 사용)
    import (
      "github.com/99designs/gqlgen/handler"
      "github.com/gin-gonic/gin"
    )
    
    // Defining the Graphql handler
    func graphqlHandler() gin.HandlerFunc {
      h := handler.GraphQL(NewExecutableSchema(Config{Resolvers: &Resolver{}}))
    
      return func(c *gin.Context) {
        h.ServeHTTP(c.Writer, c.Request)
      }
    }
    
    // Defining the Playground handler
    func playgroundHandler() gin.HandlerFunc {
      h := handler.Playground("GraphQL", "/query")
    
      return func(c *gin.Context) {
        h.ServeHTTP(c.Writer, c.Request)
      }
    }
    
    func main() {
      // Setting up Gin
      r := gin.Default()
      r.POST("/query", graphqlHandler())
      r.GET("/", playgroundHandler())
        r.Run()
    }
    

    Gorm 모형 준비
    다음에 모델 정의에 사용할 모델을 정의할 것입니다.
    나는 고 언어의 표준 레이아웃에 따라 그것을 내부/모델에 놓았다.
    // user.go
    package models
    
    import (
        "time"
    )
    
    type User struct {
        ID        int
        Name      string
        Todos     []Todo
        CreatedAt time.Time
        UpdatedAt time.Time
    }
    
    
    // todo.go
    package models
    
    import (
        "time"
    )
    
    type Todo struct {
        ID        int
        Text      string
        Done      bool
        UserID    int
        User      User
        CreatedAt time.Time
        UpdatedAt time.Time
    }
    
     
    물론, 나는 그것이 삽입하는 경향이 더 강하다고 생각한다. (Gorm 문서 참조)
    하지만 내가 지나치면 실수가 일어난다.나는 모든 속성을 썼다.(더 좋은 방법을 알고 싶다)
     
    // todo.go
    package models
    
    import (
        "time"
    )
    
    type Todo struct {
      gorm.Model
        Text      string
        Done      bool
        UserID    int
        User      User
    }
    
     
    모델의 정의를 완성하면 gqlgen에서 모델의 맵을 만들어야 합니다.yml.
    다음은 이러한 상황의 정의이다.
    models:
      Todo:
        model: gin_graphql/internal/models.Todo
      User:
        model: gin_graphql/internal/models.User
    

    정의 모드
    지금까지 제가 소개한 것은 schema drriven에서 개발할 준비를 하고 있으며, 지금부터api의 코드와 논리를 작성할 것입니다.
    type Todo {
      id:   Int!
      text: String!
      done: Boolean!
      userID: Int!
      user: User!
      createdAt: Time!
      updatedAt: Time!
    }
    
    type User {
      id: Int!
      name: String!
      createdAt: Time!
      updatedAt: Time!
    }
    
    type Query {
      todos: [Todo!]!
      users: [User!]!
      todo(input: FetchTodo): Todo!
    }
    
    input NewTodo {
      text: String!
      userId: Int!
    }
    
    input EditTodo {
      id: Int!
      text: String!
    }
    
    input NewUser {
      name: String!
    }
    
    input FetchTodo {
      id: Int!
    }
    
    type Mutation {
      createTodo(input: NewTodo!): Todo!
      updateTodo(input: EditTodo!): Todo!
      deleteTodo(input: Int!): Todo!
      createUser(input: NewUser!): User!
    }
    
    scalar Time
    
    위쪽은 패턴입니다.graphql에서 CRUD를 실현합니다.이 코드를 작성하려면graphql 문법을 사용해야 합니다.
    자원을 변경하는 과정은 돌연변이에서 쓰고, 자원을 읽는 과정은 조회에서 쓴다.
    필요한 유형도 패턴에 정의됩니다.graphql.
    지금까지 코드를 만들고 해석기를 편집하기 위해 Reday가 있습니다.파서에서 입출력은 모드로 정의됩니다.graphql, 자동으로 생성됩니다.
    그러니 입력과 출력 사이의 논리에 집중해야 한다.

    실행 파서
    해석기에서 코드를 작성하여 데이터베이스에서 데이터를 얻거나 기록을 만듭니다.
    해상도가 존재할 때 덮어쓰지 않는다는 것을 알아야 합니다.
    다음은 제가 쓴 해석기입니다.나는 모든 것을 설명할 수 없다. 왜냐하면 이것은 짐에 관한 것이기 때문에 우리에게는 매우 지루하다.
    
    type Resolver struct {
    }
    
    func (r *Resolver) Mutation() MutationResolver {
        return &mutationResolver{r}
    }
    func (r *Resolver) Query() QueryResolver {
        return &queryResolver{r}
    }
    
    type mutationResolver struct{ *Resolver }
    
    func (r *mutationResolver) CreateUser(ctx context.Context, input NewUser) (*models.User, error) {
        user := models.User{
            Name: input.Name,
        }
        db.Create(&user)
        return &user, nil
    }
    
    func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (*models.Todo, error) {
        todo := models.Todo{
            Text:   input.Text,
            UserID: input.UserID,
            Done:   false,
        }
        db.Create(&todo)
        return &todo, nil
    }
    
    func (r *mutationResolver) UpdateTodo(ctx context.Context, input EditTodo) (*models.Todo, error) {
        todo := models.Todo{ID: input.ID}
        db.First(&todo)
        todo.Text = input.Text
        db.Model(&models.Todo{}).Update(&todo)
    
        return &todo, nil
    }
    
    func (r *mutationResolver) DeleteTodo(ctx context.Context, input int) (*models.Todo, error) {
        todo := models.Todo{
            ID: input,
        }
        db.First(&todo)
        db.Delete(&todo)
        return &todo, nil
    }
    
    type queryResolver struct{ *Resolver }
    
    func (r *queryResolver) Todo(ctx context.Context, input *FetchTodo) (*models.Todo, error) {
        var todo models.Todo
        db.Preload("User").First(&todo, input.ID)
        return &todo, nil
    }
    
    func (r *queryResolver) Todos(ctx context.Context) ([]models.Todo, error) {
        var todos []models.Todo
        db.Preload("User").Find(&todos)
        fmt.Println(todos[0].User)
        return todos, nil
    }
    
    func (r *queryResolver) Users(ctx context.Context) ([]models.User, error) {
        var users []models.User
        db.Find(&users)
        return users, nil
    }
    

    뛰어다니다
    마지막으로 제가 소개한 과정을 마치면 코드를 실행할 수 있습니다.
    내가 준비한 창고의 입구점은 cmd/app/main이다.이렇게 하면 너는 아래의 명령을 실행할 수 있다.
    ENV=development go run cmd/app/main.go
    
    이 명령에서 env를 전달해야 합니다. 왜냐하면 이것은 env를 통해 설정을 불러오기 위해서입니다.
    서버를 성공적으로 실행하면 http://localhost:8080에서 시도할 수 있습니다.

    스택 점
    이어서 나는 내가 건축할 때 쌓인 점을 쓸 것이다.

    CreatedAt 및 Updated와 같은 시간 유형은 어떻게 정의합니까?
    패턴 유형은 5가지입니다.
  • 신분증
  • 내부
  • 부동
  • 문자열
  • 부울 값
  • 그래서 우리는 날짜와 같은 우리가 필요로 하는 유형을 정의해야 한다.
    이 예시를 만들 때, 나는 여기에 중첩을 했지만, gqlgen이 시간 형식을 제공했기 때문에, 나는 그것을 사용할 수 있다.
    위의 패턴 정의에 따라 이 동작을 실행하기만 하면 됩니다.
    scalar Time
    
    유형을 선언하면 유형이 Go Embedded tim으로 매핑됩니다.시간 유형.
    Gqlgen은 맵, 업로드, 모든 종류를 지원하기 때문에, 그 종류를 사용하려면, 표량 설명만 사용하면 됩니다.

    코드를 생성하는 동안 Gorm 모델에 오류가 발생했습니다.
    이 문제는 해결되지 않았다.그래도 난 변통을 통해 해결했어..
    gorm 삽입 형식으로 모델을 정의했지만 생성 과정에서 오류가 발생했습니다.
    type User {
      gorm.Model
      name
    }
    
    나는 모든 속성을 썼지만 잘 쓰지 못했다.
    만약 더 좋은 방법을 아는 사람이 있다면, 나는 나에게 가르쳐 주고 싶다.

    연관 모델 가져오기 및 목록 가져오기
    이것은 GraphQL에 관한 것이 아니라 Gorm에 관한 것이다.나는 자주 관련 모델을 포함하는 목록을 얻기 때문에, 나도 이 예시에서 그것을 시험해 보았다.
    이것은 내가 상상한 것보다 간단하다.struct에 필드만 추가하면 됩니다.
    type Todo {
      id:   Int!
      text: String!
      done: Boolean!
      userID: Int!
      user: User!
      createdAt: Time!
      updatedAt: Time!
    }
    
    예를 들어 Todo 모델을 정의할 때 Todo 모델에 사용자 ID와 사용자 필드를 추가하면 됩니다.
    db.Preload("User").Find(&todo, input.ID)
    
    이 줄을 해상도에 추가합니다.
    사전 로드 클래스를 작성하여 N+1 문제를 해결했습니다.
    내가 실행할 때, 이것이 바로 ql입니다.사용자 쿼리가 미리 로드되었는지 확인할 수 있습니다.
    (/app/src/gin_graphql/resolver.go:65)
    [2019-05-14 15:27:07]  [0.90ms]  SELECT * FROM `todos`  WHERE (`todos`.`id` = 2) ORDER BY `todos`.`id` ASC LIMIT 1
    [1 rows affected or returned ]
    
    (/app/src/gin_graphql/resolver.go:65)
    [2019-05-14 15:27:07]  [2.65ms]  SELECT * FROM `users`  WHERE (`id` IN (1)) ORDER BY `users`.`id` ASC
    [1 rows affected or returned ]
    

    머리를 쥐어짜다
    웹 API 및 Graphql의 핫스택을 구축하려고 합니다.
    내가 REST API로 시스템을 개발했을 때 나는 지금 REST API의 한계를 느꼈기 때문에 graphql의 장점이 더욱 강하다고 생각한다.
    REST API에서 새 끝점을 작성할 때 클라이언트에서 해당 끝점을 요청하려면 코드를 작성해야 합니다.
    GraphQL에서 패턴을 한 번만 정의하면 언제든지 필요한 자원을 얻을 수 있습니다.
    REST에서api를 개발할 때, 나는 어떻게 전단에 의존하지 않는지 생각했지만, 현재 단말기에 변화가 생겼을 때, 나는 항상 서버 단말기의 코드를 변경해야 한다.
    나는 이런 문제는 GraphQL을 통해 해결할 수 있다고 생각한다. (그러나 많은 사람들이 이렇게 말한다)
    물론 나는 그것에 대해 깊이 알고 있다. 나는 그것의 결점을 알고 있다. 나는 지금 영원히 모르지만, 나는 그것의 잠재력을 느낄 수 있다.
    REST에 익숙하거나 웹 API 작성에 지친 경우 GraphQL을 시도해 보십시오.
    감사합니다.

    좋은 웹페이지 즐겨찾기