Golang for Web (두 번째 부분): Gofiber REST API + Mongo DB Atlas
그 전에, 만약 당신이 이 시리즈의 첫 번째 부분을 따르지 않았다면, 한번 검사해 주십시오.
만약 네가 이미 해냈다면, 우리는 우리의 코드를 깊이 연구할 것이다.
우리는 무엇을 건설할 것입니까?🤔
이 시리즈의 첫 번째 섹션에서 Todo 애플리케이션을 계속합니다.여기에 TODO를 저장하는 데이터베이스로
Mongo DB
를 추가합니다.선결 조건📝
시작해보도록 하겠습니다.🏁
1. 프로젝트 설정
먼저 VS 코드(또는 기타 코드 편집기/IDE)에서 이전 항목을 엽니다.
현재, 모든 의존 항목을 설치하기 위해 아래 명령을 실행합니다.
go get
환경 변수를 관리하기 위해 mongo-driver
forMongo DB
와 godotenv
를 설치합니다.go get -u go.mongodb.org/mongo-driver github.com/joho/godotenv
현재 루트 디렉터리에 models
, config
디렉터리와 .env
파일을 만듭니다.우리의 루트 디렉토리는 다음과 같습니다.
.
|____config
|____controllers
| |____todo.go
|____models
|____routes
| |____todo.go
|____.env
|____go.mod
|____go.sum
|____main.go
2. Mongo DB 설정
https://www.mongodb.com/에 계정을 만들거나 기존 계정에 로그인할 수 있습니다.
현재
gofiber-todo-api
(또는 원하는 것) 이라는 새 프로젝트를 만들고 그룹을 만듭니다.잠시 기다려 주십시오. 몬고 db에서 그룹을 만들고 있습니다.
지금 클릭하여 연결
그런 다음 데이터베이스 사용자를 추가하고 접속 IP 주소 추가 에서 어디서든 액세스 허용 을 선택한 다음 IP 주소 추가 를 클릭합니다.
그런 다음 [접속 방법]에서 [응용 프로그램 연결]을 선택합니다.드라이버 드롭다운 목록에서 Go를 선택하고 버전 드롭다운 목록에서 1.4 이상을 선택합니다.
연결 문자열을 복사합니다.다음 그림과 같이
.env
파일에 붙여넣습니다.MONGO_URI=mongodb+srv://<dbUser>:<password>@cluster0.oynlp.mongodb.net/<dbname>?retryWrites=true&w=majority
여기<dbUser>
를 데이터베이스 사용자 이름으로, <password>
를 사용자 비밀번호로, <dbname>
를 데이터베이스 이름으로 바꿉니다.또한
.env
파일에 다른 두 변수를 추가하면 다음과 같다.DATABASE_NAME=<dbname>
TODO_COLLECTION=todos
여기DATABASE_NAME
는 <dbname>
의 MONGO_URI
와 같습니다.3. Mongo DB 구성
db.go
디렉터리에 config
파일을 만듭니다.현재 포장을 신고하고 다음과 같은 방식으로 우리가 필요로 하는 수입 제품을 도입합니다.
config/db.go
:package config
import (
"context"
"log"
"os"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
그리고 다음과 같이 추가MongoInstance
합니다.// MongoInstance : MongoInstance Struct
type MongoInstance struct {
Client *mongo.Client
DB *mongo.Database
}
// MI : An instance of MongoInstance Struct
var MI MongoInstance
config/db.go
:package config
import (
"context"
"fmt"
"log"
"os"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
// MongoInstance : MongoInstance Struct
type MongoInstance struct {
Client *mongo.Client
DB *mongo.Database
}
// MI : An instance of MongoInstance Struct
var MI MongoInstance
이제 모든 연결 설정으로 ConnectDB()
방법을 만듭니다.// ConnectDB - database connection
func ConnectDB() {
client, err := mongo.NewClient(options.Client().ApplyURI(os.Getenv("MONGO_URI")))
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
err = client.Ping(ctx, readpref.Primary())
if err != nil {
log.Fatal(err)
}
fmt.Println("Database connected!")
MI = MongoInstance{
Client: client,
DB: client.Database(os.Getenv("DATABASE_NAME")),
}
}
main.go
로 이동하여 다음 패키지를 가져옵니다.import (
"log" // new
"github.com/devsmranjan/golang-fiber-basic-todo-app/config" // new
"github.com/devsmranjan/golang-fiber-basic-todo-app/routes"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/joho/godotenv" // new
)
main()
내부 가동dotenv
은 다음과 같다.// dotenv
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
db 설정 방법 호출ConnectDB()
// config db
config.ConnectDB()
현재 우리main()
는 다음과 같이 보일 것이다.func main() {
app := fiber.New()
app.Use(logger.New())
// dotenv
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
// config db
config.ConnectDB()
// setup routes
setupRoutes(app)
// Listen on server 8000 and catch error if any
err = app.Listen(":8000")
// handle error
if err != nil {
panic(err)
}
}
이제 우리 프로그램을 실행합시다go run main.go
다음 출력을 받을 것입니다Database connected!
┌───────────────────────────────────────────────────┐
│ Fiber v2.2.0 │
│ http://127.0.0.1:8000 │
│ │
│ Handlers ............ 12 Threads ............. 4 │
│ Prefork ....... Disabled PID ............. 55680 │
└───────────────────────────────────────────────────┘
이것은 우리의 데이터베이스가 성공적으로 연결되었다는 것을 의미한다.추가: 서버를 자동으로 다시 로드하려면 컴퓨터에 전역적으로 설치air할 수 있습니다.
현재 프로그램을 실행하려면 루트 디렉터리에서 실행하십시오
air
또는 디버그 모드에서 실행할 수 있습니다air -d
4. 모델 생성
현재
todo.go
디렉터리에 models
라는 파일을 만듭니다.우리를 창설
Todo
한 것은 다음과 같다.package models
import (
"time"
)
// Todo - todo model
type Todo struct {
ID *string `json:"id,omitempty" bson:"_id,omitempty"`
Title *string `json:"title"`
Completed *bool `json:"completed"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
5. 컨트롤러에 데이터베이스 연결
이제
controllers/todo.go
로 이동하고 다음 코드를 먼저 삭제합니다.- // Todo : todo model
- type Todo struct {
- ID int `json:"id"`
- Title string `json:"title"`
- Completed bool `json:"completed"`
- }
-
- var todos = []*Todo{
- {
- ID: 1,
- Title: "Walk the dog 🦮",
- Completed: false,
- },
- {
- ID: 2,
- Title: "Walk the cat 🐈",
- Completed: false,
- },
- }
필요한 패키지 가져오기import (
"os" // new
"strconv"
"github.com/devsmranjan/golang-fiber-basic-todo-app/config" // new
"github.com/devsmranjan/golang-fiber-basic-todo-app/models" // new
"github.com/gofiber/fiber/v2"
"go.mongodb.org/mongo-driver/bson" // new
"go.mongodb.org/mongo-driver/bson/primitive" // new
"go.mongodb.org/mongo-driver/mongo" // new
)
5.1-GetTodos()
+ todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
+
+ // Query to filter
+ query := bson.D{{}}
+
+ cursor, err := todoCollection.Find(c.Context(), query)
+
+ if err != nil {
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "success": false,
+ "message": "Something went wrong",
+ "error": err.Error(),
+ })
+ }
+
+ var todos []models.Todo = make([]models.Todo, 0)
+
+ // iterate the cursor and decode each item into a Todo
+ err = cursor.All(c.Context(), &todos)
+ if err != nil {
+ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
+ "success": false,
+ "message": "Something went wrong",
+ "error": err.Error(),
+ })
+ }
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todos": todos,
},
})
현재GetTodos()
:// GetTodos : get all todos
func GetTodos(c *fiber.Ctx) error {
todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// Query to filter
query := bson.D{{}}
cursor, err := todoCollection.Find(c.Context(), query)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "Something went wrong",
"error": err.Error(),
})
}
var todos []models.Todo = make([]models.Todo, 0)
// iterate the cursor and decode each item into a Todo
err = cursor.All(c.Context(), &todos)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"message": "Something went wrong",
"error": err.Error(),
})
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todos": todos,
},
})
}
5.2-CreateTodo()
+ todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
+
- type Request struct {
- Title string `json:"title"`
- }
-
- var data Request
+
+ data := new(models.Todo)
err := c.BodyParser(&body)
// if error
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse JSON",
"error": err,
})
}
-
- // create a todo variable
- todo := &Todo{
- ID: len(todos) + 1,
- Title: body.Title,
- Completed: false,
- }
-
- // append in todos
- todos = append(todos, todo)
-
+ data.ID = nil
+ f := false
+ data.Completed = &f
+ data.CreatedAt = time.Now()
+ data.UpdatedAt = time.Now()
+
+ result, err := todoCollection.InsertOne(c.Context(), data)
+
+ if err != nil {
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "success": false,
+ "message": "Cannot insert todo",
+ "error": err,
+ })
+ }
+
+ // get the inserted data
+ todo := &models.Todo{}
+ query := bson.D{{Key: "_id", Value: result.InsertedID}}
+
+ todoCollection.FindOne(c.Context(), query).Decode(todo)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todo": todo,
},
})
지금CreateTodo()
보아하니:// CreateTodo : Create a todo
func CreateTodo(c *fiber.Ctx) error {
todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
data := new(models.Todo)
err := c.BodyParser(&data)
// if error
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse JSON",
"error": err,
})
}
data.ID = nil
f := false
data.Completed = &f
data.CreatedAt = time.Now()
data.UpdatedAt = time.Now()
result, err := todoCollection.InsertOne(c.Context(), data)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot insert todo",
"error": err,
})
}
// get the inserted data
todo := &models.Todo{}
query := bson.D{{Key: "_id", Value: result.InsertedID}}
todoCollection.FindOne(c.Context(), query).Decode(todo)
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todo": todo,
},
})
}
5.3-GetTodo()
+ todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// get parameter value
paramID := c.Params("id")
// convert parameter value string to int
- id, err := strconv.Atoi(paramID)
+ id, err := primitive.ObjectIDFromHex(paramID)
// if error in parsing string to int
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse Id",
"error": err,
})
}
// find todo and return
- for _, todo := range todos {
- if todo.ID == id {
- return c.Status(fiber.StatusOK).JSON(fiber.Map{
- "success": true,
- "data": fiber.Map{
- "todo": todo,
- },
- })
- }
- }
-
- // if todo not available
- return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
- "success": false,
- "message": "Todo not found",
- })
+
+ todo := &models.Todo{}
+
+ query := bson.D{{Key: "_id", Value: id}}
+
+ err = todoCollection.FindOne(c.Context(), query).Decode(todo)
+
+ if err != nil {
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+ "success": false,
+ "message": "Todo Not found",
+ "error": err,
+ })
+ }
+
+ return c.Status(fiber.StatusOK).JSON(fiber.Map{
+ "success": true,
+ "data": fiber.Map{
+ "todo": todo,
+ },
+ })
지금GetTodo()
보아하니:// GetTodo : get a single todo
// PARAM: id
func GetTodo(c *fiber.Ctx) error {
todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// get parameter value
paramID := c.Params("id")
// convert parameterID to objectId
id, err := primitive.ObjectIDFromHex(paramID)
// if error while parsing paramID
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse Id",
"error": err,
})
}
// find todo and return
todo := &models.Todo{}
query := bson.D{{Key: "_id", Value: id}}
err = todoCollection.FindOne(c.Context(), query).Decode(todo)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Todo Not found",
"error": err,
})
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todo": todo,
},
})
}
5.4-UpdateTodo()
+ todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// find parameter
paramID := c.Params("id")
// convert parameter string to int
- id, err := strconv.Atoi(paramID)
// if parameter cannot parse
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse id",
"error": err,
})
}
-
- // request structure
- type Request struct {
- Title *string `json:"title"`
- Completed *bool `json:"completed"`
- }
var data Request
err = c.BodyParser(&data)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse JSON",
"error": err,
})
}
-
- var todo *Todo
-
- for _, t := range todos {
- if t.ID == id {
- todo = t
- break
- }
- }
-
- if todo.ID == 0 {
- return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
- "success": false,
- "message": "Not found",
- })
- }
+
+ query := bson.D{{Key: "_id", Value: id}}
+
+ // updateData
+ var dataToUpdate bson.D
if data.Title != nil {
- todo.Title = *data.Title
+ dataToUpdate = append(dataToUpdate, bson.E{Key: "title", Value: data.Title})
}
if data.Completed != nil {
- todo.Completed = *data.Completed
+ dataToUpdate = append(dataToUpdate, bson.E{Key: "completed", Value: data.Completed})
}
+
+ dataToUpdate = append(dataToUpdate, bson.E{Key: "updatedAt", Value: time.Now()})
+
+ update := bson.D{
+ {Key: "$set", Value: dataToUpdate},
+ }
+
+ // update
+ err = todoCollection.FindOneAndUpdate(c.Context(), query, update).Err()
+
+ if err != nil {
+ if err == mongo.ErrNoDocuments {
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+ "success": false,
+ "message": "Todo Not found",
+ "error": err,
+ })
+ }
+
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "success": false,
+ "message": "Cannot update todo",
+ "error": err,
+ })
+ }
+
+ // get updated data
+ todo := &models.Todo{}
+
+ todoCollection.FindOne(c.Context(), query).Decode(todo)
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todo": todo,
},
})
지금UpdateTodo()
보아하니:// UpdateTodo : Update a todo
// PARAM: id
func UpdateTodo(c *fiber.Ctx) error {
todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// find parameter
paramID := c.Params("id")
// convert parameterID to objectId
id, err := primitive.ObjectIDFromHex(paramID)
// if parameter cannot parse
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse id",
"error": err,
})
}
// var data Request
data := new(models.Todo)
err = c.BodyParser(&data)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse JSON",
"error": err,
})
}
query := bson.D{{Key: "_id", Value: id}}
// updateData
var dataToUpdate bson.D
if data.Title != nil {
// todo.Title = *data.Title
dataToUpdate = append(dataToUpdate, bson.E{Key: "title", Value: data.Title})
}
if data.Completed != nil {
// todo.Completed = *data.Completed
dataToUpdate = append(dataToUpdate, bson.E{Key: "completed", Value: data.Completed})
}
dataToUpdate = append(dataToUpdate, bson.E{Key: "updatedAt", Value: time.Now()})
update := bson.D{
{Key: "$set", Value: dataToUpdate},
}
// update
err = todoCollection.FindOneAndUpdate(c.Context(), query, update).Err()
if err != nil {
if err == mongo.ErrNoDocuments {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Todo Not found",
"error": err,
})
}
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot update todo",
"error": err,
})
}
// get updated data
todo := &models.Todo{}
todoCollection.FindOne(c.Context(), query).Decode(todo)
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
"data": fiber.Map{
"todo": todo,
},
})
}
5.5 - TODO 삭제()
+ todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// get param
paramID := c.Params("id")
-
- // convert param string to int
+ // convert parameter to object id
- id, err := strconv.Atoi(paramID)
+ id, err := primitive.ObjectIDFromHex(paramID)
// if parameter cannot parse
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse id",
"error": err,
})
}
// find and delete todo
- for i, todo := range todos {
- if todo.ID == id {
-
- todos = append(todos[:i], todos[i+1:]...)
-
- return c.SendStatus(fiber.StatusNoContent)
- }
- }
+
+ query := bson.D{{Key: "_id", Value: id}}
+
+ err = todoCollection.FindOneAndDelete(c.Context(), query).Err()
+
+ if err != nil {
+ if err == mongo.ErrNoDocuments {
+ return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
+ "success": false,
+ "message": "Todo Not found",
+ "error": err,
+ })
+ }
+
+ return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
+ "success": false,
+ "message": "Cannot delete todo",
+ "error": err,
+ })
+ }
-
- // if todo not found
- return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
- "success": false,
- "message": "Todo not found",
- })
+
+ return c.SendStatus(fiber.StatusNoContent)
지금DeleteTodo()
보아하니:// DeleteTodo : Delete a todo
// PARAM: id
func DeleteTodo(c *fiber.Ctx) error {
todoCollection := config.MI.DB.Collection(os.Getenv("TODO_COLLECTION"))
// get param
paramID := c.Params("id")
// convert parameter to object id
id, err := primitive.ObjectIDFromHex(paramID)
// if parameter cannot parse
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot parse id",
"error": err,
})
}
// find and delete todo
query := bson.D{{Key: "_id", Value: id}}
err = todoCollection.FindOneAndDelete(c.Context(), query).Err()
if err != nil {
if err == mongo.ErrNoDocuments {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Todo Not found",
"error": err,
})
}
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"message": "Cannot delete todo",
"error": err,
})
}
return c.SendStatus(fiber.StatusNoContent)
}
놀라운!!!지금까지 우리는 아주 잘했다.🤝6. 우리의 단점 테스트🧪
Postman에서 API를 테스트해 보겠습니다.그 전에 서버를 실행하는 것을 잊지 마세요.
처리해야 할 모든 사항을 보려면
GET
에 localhost:8000/api/todos
요청하십시오.새 todo를 만들려면
POST
에 localhost:8000/api/todos
요청을 하십시오. 요청 본문에 title: <String>
가 있습니다.이제 다음과 같이 더 많은 업무 항목을 추가할 수 있습니다.
예!이거 신기하지 않아요?🤩
현재 id를 통해 todo를 가져오려면
GET
에 localhost:8000/api/todos/:id
요청을 하십시오여기는 todo
:id
로 교체id
합니다.현재, 요청 본문에서
PUT
또는 localhost:8000/api/todos/:id
또는 둘을 동시 title: <String>
로 completed: <Boolean>
요청하도록 todo를 업데이트합니다.여기는 todo
:id
로 교체id
합니다.처리해야 할 사항을 삭제하려면
DELETE
에 localhost:8000/api/todos/:id
요청하십시오.여기는 todo
:id
로 교체id
합니다.축하합니다.🥳 🥳 🥳 저희가 해냈어요.💪🏻
결론📋
gofiber에 대한 더 많은 정보는 여기 문서를 더욱 깊이 있게 보시기 바랍니다 https://docs.gofiber.io/
Mongo DB 관계자documentation.
이것은 나의 GitHub이 이 프로젝트에 연결된 것이다 - https://github.com/devsmranjan/golang-fiber-basic-todo-app/
제 글을 읽어주셔서 감사합니다.🙂 . 나는 네가 이곳에서 약간의 것을 배웠으면 한다.
만약 당신이 이 프로젝트를 개선하고 싶다면 환매here를 진행할 수 있다.
즐거움 코드👨💻👩💻 이 시리즈의 다음 댓글을 계속 지켜봐 주세요!
감사합니다!인사하는 거 잊지 마세요.♥️ 다음:)
Reference
이 문제에 관하여(Golang for Web (두 번째 부분): Gofiber REST API + Mongo DB Atlas), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/devsmranjan/golang-for-web-part-ii-gofiber-rest-api-mongo-db-atlas-2d1i텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)