使用 Golang 和 MongoDB 构建 REST API - Fiber 版本
先决条件
让我们开始编程吧
结论
表述性状态转移(REST)是一种架构模式,它指导应用程序编程接口(API)的设计和开发。REST API 已成为产品服务器端与其客户端之间通信的标准,以提高性能、可扩展性、简洁性、可修改性、可见性、可移植性和可靠性。
本文将探讨如何使用 Golang、Fiber框架和MongoDB构建用户管理应用程序。在本教程的最后,我们将学习如何构建 Fiber 应用程序、创建 REST API 以及使用 MongoDB 持久化数据。
Fiber 是一个受Express启发、用 Golang 编写的 HTTP Web 框架,它具有高性能和零内存分配支持。Fiber 构建于Fasthttp之上,Fasthttp 本身也是一个用 Golang 编写的 HTTP 引擎。
MongoDB 是一款基于文档的数据库管理程序,可作为关系型数据库的替代方案。MongoDB 支持处理大型分布式数据集,并提供无缝存储和检索信息的选项。
您可以在此存储库中找到完整的源代码。
先决条件
本文后续步骤需要具备 Golang 编程经验。虽然不要求具备 MongoDB 经验,但有相关经验会更好。
我们还需要以下物品:
- 用于托管数据库的MongoDB 账户。注册 完全免费。
- Postman或您选择的任何 API 测试应用程序
让我们开始编程吧
入门
首先,我们需要导航到目标目录,然后在终端中运行以下命令。
mkdir fiber-mongo-api && cd fiber-mongo-api
该命令会创建一个fiber-mongo-api文件夹并导航到项目目录。
接下来,我们需要初始化一个 Go 模块来管理项目依赖项,运行以下命令:
go mod init fiber-mongo-api
此命令将创建一个go.mod用于跟踪项目依赖关系的文件。
接下来,我们使用以下命令安装所需的依赖项:
go get -u github.com/gofiber/fiber/v2 go.mongodb.org/mongo-driver/mongo github.com/joho/godotenv github.com/go-playground/validator/v10
github.com/gofiber/fiber/v2是一个用于构建 Web 应用程序的框架。
go.mongodb.org/mongo-driver/mongo是用于连接 MongoDB 的驱动程序。
github.com/joho/godotenv是一个用于管理环境变量的库。
github.com/go-playground/validator/v10是一个用于验证结构体和字段的库。
github.com/klauspost/compress is not in your go.mod filego mod tidy安装完所需的依赖项后,文件中可能会出现错误go.mod。要解决此问题,我们需要手动安装所需的软件包。
go get github.com/klauspost/compress
应用程序入口点
项目依赖项安装完毕后,我们需要main.go在根目录下创建一个文件,并添加以下代码片段:
package main
import (
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(&fiber.Map{"data": "Hello from Fiber & mongoDB"})
})
app.Listen(":6000")
}
上面的代码片段执行以下操作:
- 导入所需的依赖项。
- 使用该函数初始化 Fiber 应用程序
New。 - 使用该
Get函数路由到/路径和返回 JSON 的处理函数Hello from Fiber & mongoDB。fiber.Map是的快捷方式map[string]interface{},对 JSON 返回很有用。 - 设置应用程序监听端口
6000。
接下来,我们可以通过在终端中运行以下命令来启动开发服务器,从而测试我们的应用程序。
go run main.go
Golang 中的模块化
良好的项目文件夹结构至关重要。合理的项目结构可以简化我们管理应用程序依赖项的方式,并使我们自己和其他人更容易阅读代码库。
为此,我们需要在项目目录中创建 `<module> configs`、 `<module> ` controllers、 `<module> ` 和`<module>` 文件夹。modelsresponsesroutes
PS:该 go.sum 文件包含所有依赖项校验和,并由 go 工具自动管理,我们无需担心。
configs用于模块化项目配置文件
controllers是用于模块化应用程序逻辑。
models是用于数据和数据库逻辑的模块化。
responses是用来模块化描述我们希望 API 返回的响应的文件。这一点稍后会更清楚。
routes用于模块化 URL 模式和处理程序信息。
设置 MongoDB
完成上述步骤后,我们需要登录或注册MongoDB帐户。点击项目下拉菜单,然后点击“新建项目”按钮。
输入golang-api项目名称,点击“下一步”,然后点击“创建项目”。
点击“构建数据库”
选择“共享”作为数据库类型。
点击“创建”按钮设置集群。设置过程可能需要一些时间。
接下来,我们需要创建一个用户,以便从外部访问数据库。输入用户名和密码,然后点击“创建用户”。我们还需要添加自己的 IP 地址,以便安全地连接到数据库。点击“添加我的当前 IP 地址”按钮即可。然后点击“完成并关闭”保存更改。
保存更改后,应该会看到如下所示的“数据库部署”屏幕:
将我们的应用程序连接到 MongoDB
配置完成后,我们需要将应用程序连接到已创建的数据库。为此,请单击“连接”按钮。
点击“连接您的应用程序”,将驱动程序和Go版本更改为如下所示的内容。然后点击复制图标复制连接字符串。
设置环境变量
接下来,我们需要修改复制的连接字符串,使其包含我们之前创建的用户密码,并更改数据库名称。为此,首先需要.env在根目录下创建一个文件,并在该文件中添加以下代码片段:
MONGOURI=mongodb+srv://<YOUR USERNAME HERE>:<YOUR PASSWORD HERE>@cluster0.e5akf.mongodb.net/myFirstDatabese?retryWrites=true&w=majority
以下是一个正确填写的连接字符串示例:
MONGOURI=mongodb+srv://malomz:malomzPassword@cluster0.e5akf.mongodb.net/golangDB?retryWrites=true&w=majority
加载环境变量
完成上述步骤后,我们需要创建一个辅助函数,使用github.com/joho/godotenv之前安装的库来加载环境变量。为此,我们需要导航到该configs文件夹,并在该文件夹中创建一个env.go文件,然后添加以下代码片段:
package configs
import (
"log"
"os"
"github.com/joho/godotenv"
)
func EnvMongoURI() string {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
return os.Getenv("MONGOURI")
}
上面的代码片段执行以下操作:
- 导入所需的依赖项。
- 创建一个
EnvMongoURI函数,检查环境变量是否已正确加载,并返回该环境变量。
连接到 MongoDB
要从我们的应用程序连接到 MongoDB 数据库,首先需要导航到该configs文件夹,并在该文件夹中创建一个setup.go文件,然后添加以下代码片段:
package configs
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func ConnectDB() *mongo.Client {
client, err := mongo.NewClient(options.Client().ApplyURI(EnvMongoURI()))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
//ping the database
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB")
return client
}
//Client instance
var DB *mongo.Client = ConnectDB()
//getting database collections
func GetCollection(client *mongo.Client, collectionName string) *mongo.Collection {
collection := client.Database("golangAPI").Collection(collectionName)
return collection
}
上面的代码片段执行以下操作:
- 导入所需的依赖项。
- 创建一个
ConnectDB函数,首先配置客户端使用正确的 URI 并检查错误。其次,我们定义了一个 10 秒的连接超时时间。第三,检查连接数据库时是否存在错误,如果连接时间超过 10 秒则取消连接。最后,我们 ping 了数据库以测试连接,并返回了实例client。 - 创建一个
DB变量实例ConnectDB。这在创建集合时会非常有用。 - 创建一个函数,用于在数据库中
GetCollection检索和创建数据。collections
接下来,我们需要在应用程序启动时连接到数据库。为此,我们需要main.go按如下所示进行修改:
package main
import (
"fiber-mongo-api/configs" //add this
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//run database
configs.ConnectDB()
app.Listen(":6000")
}
设置 API 路由处理程序和响应类型
路由处理程序
完成上述步骤后,我们需要在文件夹user_route.go内创建一个文件routes来管理应用程序中所有与用户相关的路由,如下所示:
package routes
import "github.com/gofiber/fiber/v2"
func UserRoute(app *fiber.App) {
//All routes related to users comes here
}
接下来,我们需要将新创建的路由附加到http.Server,具体main.go操作如下所示:
package main
import (
"fiber-mongo-api/configs"
"fiber-mongo-api/routes" //add this
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
//run database
configs.ConnectDB()
//routes
routes.UserRoute(app) //add this
app.Listen(":6000")
}
接下来,我们需要创建一个可重用的响应类型描述struct符来描述 API 的响应。为此,请导航到该responses文件夹,并在其中创建一个user_response.go文件,然后添加以下代码片段:
package responses
import "github.com/gofiber/fiber/v2"
type UserResponse struct {
Status int `json:"status"`
Message string `json:"message"`
Data *fiber.Map `json:"data"`
}
上面的代码片段创建了一个UserResponse包含`<struct>` Status、Message`<property>` 和Data`<property>` 属性的结构体,用于表示 API 响应类型。
PS:json:"status"`<string>`、 json:"message"`<string>` 和 `<string>` json:"data" 被称为 结构体标签。结构体标签允许我们将元信息附加到相应的结构体属性上。换句话说,我们使用它们来重新格式化 API 返回的 JSON 响应。
最后,创建 REST API
接下来,我们需要一个模型来表示我们的应用程序数据。为此,我们需要导航到该models文件夹,并在该文件夹中创建一个user_model.go文件,然后添加以下代码片段:
package models
import "go.mongodb.org/mongo-driver/bson/primitive"
type User struct {
Id primitive.ObjectID `json:"id,omitempty"`
Name string `json:"name,omitempty" validate:"required"`
Location string `json:"location,omitempty" validate:"required"`
Title string `json:"title,omitempty" validate:"required"`
}
上面的代码片段执行以下操作:
- 导入所需的依赖项。
- 创建一个
User包含必需属性的结构体。我们在结构体标签中添加了 `--no-text`omitempty和validate:"required"`--no-text`,分别告诉 Fiber 忽略空字段并将该字段设为必需字段。
创建用户端点
模型设置完成后,我们现在可以创建一个创建用户的函数。为此,我们需要导航到该controllers文件夹,并在该文件夹中创建一个user_controller.go文件,然后添加以下代码片段:
package controllers
import (
"context"
"fiber-mongo-api/configs"
"fiber-mongo-api/models"
"fiber-mongo-api/responses"
"net/http"
"time"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var user models.User
defer cancel()
//validate the request body
if err := c.BodyParser(&user); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//use the validator library to validate required fields
if validationErr := validate.Struct(&user); validationErr != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": validationErr.Error()}})
}
newUser := models.User{
Id: primitive.NewObjectID(),
Name: user.Name,
Location: user.Location,
Title: user.Title,
}
result, err := userCollection.InsertOne(ctx, newUser)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
return c.Status(http.StatusCreated).JSON(responses.UserResponse{Status: http.StatusCreated, Message: "success", Data: &fiber.Map{"data": result}})
}
上面的代码片段执行以下操作:
- 导入所需的依赖项。
- 分别使用我们之前安装的库创建集合和验证模型的变量
userCollection。validategithub.com/go-playground/validator/v10 - 创建一个
CreateUser返回值的函数error。在函数内部,我们首先定义了向文档插入用户时 10 秒的超时时间,并使用验证器库验证请求体和必填字段。我们使用UserResponse之前创建的结构体返回相应的消息和状态码。其次,我们创建了一个newUser变量,使用该函数插入它userCollection.InsertOne,并检查是否存在错误。最后,如果插入成功,我们返回正确的响应。
接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:
package routes
import (
"fiber-mongo-api/controllers" //add this
"github.com/gofiber/fiber/v2"
)
func UserRoute(app *fiber.App) {
app.Post("/user", controllers.CreateUser) //add this
}
获取用户端点
要获取用户详细信息,我们需要user_controller.go按如下所示进行修改:
package controllers
import (
//other import goes here
"go.mongodb.org/mongo-driver/bson" //add this
)
var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
//create user code goes here
}
func GetAUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Params("userId")
var user models.User
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&user)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
return c.Status(http.StatusOK).JSON(responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": user}})
}
上面的代码片段执行以下操作:
- 导入所需的依赖项。
- 创建一个
GetAUser返回一个对象的函数error。在函数内部,我们首先定义了在文档中查找用户的超时时间(10 秒),一个用于从 URL 参数中userId获取用户IDuser的变量,以及一个用于将 URL 参数中的字符串转换userId为primitive.ObjectIDMongoDB 使用的 BSON 类型的变量。其次,我们使用 URL 参数搜索用户userCollection.FindOne,并将 URLobjId作为过滤器传递,然后使用Decode属性方法获取相应的对象。最后,我们返回解码后的响应。
接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:
package routes
import (
//import goes here
)
func UserRoute(app *fiber.App) {
//other routes goes here
app.Get("/user/:userId", controllers.GetAUser) //add this
}
PS: 我们还 userId 向 URL 路径传递了一个参数。该参数必须与我们在控制器中指定的参数一致。
编辑用户端点
要编辑用户,我们需要user_controller.go按如下所示进行修改:
package controllers
import (
//other import goes here
)
var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
//create user code goes here
}
func GetAUser(c *fiber.Ctx) error {
//get a user code goes here
}
func EditAUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Params("userId")
var user models.User
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
//validate the request body
if err := c.BodyParser(&user); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//use the validator library to validate required fields
if validationErr := validate.Struct(&user); validationErr != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": validationErr.Error()}})
}
update := bson.M{"name": user.Name, "location": user.Location, "title": user.Title}
result, err := userCollection.UpdateOne(ctx, bson.M{"id": objId}, bson.M{"$set": update})
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//get updated user details
var updatedUser models.User
if result.MatchedCount == 1 {
err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&updatedUser)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
}
return c.Status(http.StatusOK).JSON(responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": updatedUser}})
}
上面的函数EditAUser与原函数功能相同CreateUser。但是,我们添加了一个update变量来获取更新后的字段,并使用该变量更新了集合userCollection.UpdateOne。最后,我们搜索了更新后的用户详细信息,并返回了解码后的响应。
接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:
package routes
import (
//import goes here
)
func UserRoute(app *fiber.App) {
//other routes goes here
app.Put("/user/:userId", controllers.EditAUser) //add this
}
删除用户端点
要删除用户,我们需要user_controller.go按如下所示进行修改:
package controllers
import (
//other import goes here
)
var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
//create user code goes here
}
func GetAUser(c *fiber.Ctx) error {
//get a user code goes here
}
func EditAUser(c *fiber.Ctx) error {
//edit a user code goes here
}
func DeleteAUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Params("userId")
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
result, err := userCollection.DeleteOne(ctx, bson.M{"id": objId})
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
if result.DeletedCount < 1 {
return c.Status(http.StatusNotFound).JSON(
responses.UserResponse{Status: http.StatusNotFound, Message: "error", Data: &fiber.Map{"data": "User with specified ID not found!"}},
)
}
return c.Status(http.StatusOK).JSON(
responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": "User successfully deleted!"}},
)
}
该DeleteAUser函数遵循之前的步骤,使用删除匹配的记录userCollection.DeleteOne。我们还检查了项目是否已成功删除,并返回了相应的响应。
接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:
package routes
import (
//import goes here
)
func UserRoute(app *fiber.App) {
//other routes goes here
app.Delete("/user/:userId", controllers.DeleteAUser) //add this
}
获取用户列表端点
要获取用户列表,我们需要user_controller.go按如下所示进行修改:
package controllers
import (
//other import goes here
)
var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
//create user code goes here
}
func GetAUser(c *fiber.Ctx) error {
//get a user code goes here
}
func EditAUser(c *fiber.Ctx) error {
//edit a user code goes here
}
func DeleteAUser(c *fiber.Ctx) error {
//delete a user code goes here
}
func GetAllUsers(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var users []models.User
defer cancel()
results, err := userCollection.Find(ctx, bson.M{})
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//reading from the db in an optimal way
defer results.Close(ctx)
for results.Next(ctx) {
var singleUser models.User
if err = results.Decode(&singleUser); err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
users = append(users, singleUser)
}
return c.Status(http.StatusOK).JSON(
responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": users}},
)
}
该GetAllUsers函数遵循之前的步骤,通过获取用户列表来实现。我们还使用属性方法遍历返回的用户列表,userCollection.Find从而以最优方式读取该列表。Next
接下来,我们需要更新user_routes.go路由 API URL 和相应的控制器,如下所示:
package routes
import (
//import goes here
)
func UserRoute(app *fiber.App) {
//other routes goes here
app.Get("/users", controllers.GetAllUsers) //add this
}
完成 user_controller.go
package controllers
import (
"context"
"fiber-mongo-api/configs"
"fiber-mongo-api/models"
"fiber-mongo-api/responses"
"net/http"
"time"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var userCollection *mongo.Collection = configs.GetCollection(configs.DB, "users")
var validate = validator.New()
func CreateUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var user models.User
defer cancel()
//validate the request body
if err := c.BodyParser(&user); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//use the validator library to validate required fields
if validationErr := validate.Struct(&user); validationErr != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": validationErr.Error()}})
}
newUser := models.User{
Id: primitive.NewObjectID(),
Name: user.Name,
Location: user.Location,
Title: user.Title,
}
result, err := userCollection.InsertOne(ctx, newUser)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
return c.Status(http.StatusCreated).JSON(responses.UserResponse{Status: http.StatusCreated, Message: "success", Data: &fiber.Map{"data": result}})
}
func GetAUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Params("userId")
var user models.User
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&user)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
return c.Status(http.StatusOK).JSON(responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": user}})
}
func EditAUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Params("userId")
var user models.User
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
//validate the request body
if err := c.BodyParser(&user); err != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//use the validator library to validate required fields
if validationErr := validate.Struct(&user); validationErr != nil {
return c.Status(http.StatusBadRequest).JSON(responses.UserResponse{Status: http.StatusBadRequest, Message: "error", Data: &fiber.Map{"data": validationErr.Error()}})
}
update := bson.M{"name": user.Name, "location": user.Location, "title": user.Title}
result, err := userCollection.UpdateOne(ctx, bson.M{"id": objId}, bson.M{"$set": update})
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//get updated user details
var updatedUser models.User
if result.MatchedCount == 1 {
err := userCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&updatedUser)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
}
return c.Status(http.StatusOK).JSON(responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": updatedUser}})
}
func DeleteAUser(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Params("userId")
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
result, err := userCollection.DeleteOne(ctx, bson.M{"id": objId})
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
if result.DeletedCount < 1 {
return c.Status(http.StatusNotFound).JSON(
responses.UserResponse{Status: http.StatusNotFound, Message: "error", Data: &fiber.Map{"data": "User with specified ID not found!"}},
)
}
return c.Status(http.StatusOK).JSON(
responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": "User successfully deleted!"}},
)
}
func GetAllUsers(c *fiber.Ctx) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var users []models.User
defer cancel()
results, err := userCollection.Find(ctx, bson.M{})
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
//reading from the db in an optimal way
defer results.Close(ctx)
for results.Next(ctx) {
var singleUser models.User
if err = results.Decode(&singleUser); err != nil {
return c.Status(http.StatusInternalServerError).JSON(responses.UserResponse{Status: http.StatusInternalServerError, Message: "error", Data: &fiber.Map{"data": err.Error()}})
}
users = append(users, singleUser)
}
return c.Status(http.StatusOK).JSON(
responses.UserResponse{Status: http.StatusOK, Message: "success", Data: &fiber.Map{"data": users}},
)
}
完成 user_route.go
package routes
import (
"fiber-mongo-api/controllers"
"github.com/gofiber/fiber/v2"
)
func UserRoute(app *fiber.App) {
app.Post("/user", controllers.CreateUser)
app.Get("/user/:userId", controllers.GetAUser)
app.Put("/user/:userId", controllers.EditAUser)
app.Delete("/user/:userId", controllers.DeleteAUser)
app.Get("/users", controllers.GetAllUsers)
}
完成上述步骤后,我们可以通过在终端中运行以下命令来启动开发服务器,从而测试我们的应用程序。
go run main.go
结论
本文讨论了如何构建 Fiber 应用程序、构建 REST API 以及使用 MongoDB 持久化数据。
您可能会发现以下资源很有用:
文章来源:https://dev.to/hackmamba/build-a-rest-api-with-golang-and-mongodb-fibre-version-4la0





















