使用 Fiber 和 MongoDB 构建 Go REST API
在本教程中,我们将创建一个 Movie Catchphrase API,允许您创建、读取、更新和删除 Catchphrases,或者简而言之,执行 CRUD 操作。
我们将使用 Fiber(一个 Go Web 框架)和 go mongo 驱动程序来与 MongoDB 实例交互。
对于这个项目,我假设您已经将 go 安装到您的计算机上。您可以通过在终端中运行以下命令来验证安装:
go
进入全屏模式 退出全屏模式
如果还没有安装,可以到golang 官网下载必要的安装程序。
MongoDB 设置
对于这个项目,我假设您已经设置了一个 MongoDB 集群(或本地 MongoDB 安装)并拥有连接 URI。如果没有,您可以参考以下链接获取安装指南:MongoDB cluster或MongoDB local
项目设置
我们需要做的第一件事是通过为项目创建一个文件夹来设置项目。在该文件夹中,我们将使用 go mod 初始化项目并安装我们将要使用的包。运行以下命令来设置项目:
go mod init
go get -u github.com/gofiber/fiber/v2
go get go.mongodb.org/mongo-driver/mongo
go get github.com/joho/godotenv
进入全屏模式 退出全屏模式
godotenv将允许我们从.env文件中提取环境变量。在项目根目录下创建.env文件,添加以下内容:
MONGO_URI=Your_MongoDB_URI_comes_here
DB=Your_DB_name_comes_here
PORT=3000
APP_ENV=development
进入全屏模式 退出全屏模式
接下来,让我们在项目的根目录下创建一个.gitignore文件,并添加以下内容:
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
.env
进入全屏模式 退出全屏模式
开始构建API
让我们在项目的根目录下创建一个main.go文件。这将包含带有基本路由的基本服务器设置。将以下内容添加到文件中:
package main
import "github.com/gofiber/fiber/v2"
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World 👋!")
})
app.Listen(":3000")
}
进入全屏模式 退出全屏模式
为了启动应用程序,运行以下命令:
go run main.go
进入全屏模式 退出全屏模式
在浏览器中导航到localhost:3000以查看应用程序。
配置并连接到数据库
始终将应用程序的所有配置保存在单独的文件夹中。让我们在应用程序的根文件夹中创建一个新文件夹config来保存所有配置。
在 config 文件夹中创建一个新文件db.go,其内容如下:
package config
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/joho/godotenv"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type MongoInstance struct {
Client *mongo.Client
DB *mongo.Database
}
var MI MongoInstance
func ConnectDB() {
if os.Getenv("APP_ENV") != "production" {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
}
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("DB")),
}
}
进入全屏模式 退出全屏模式
我们将使用 MI 对象来查询集合。
创建流行语模型
让我们在应用程序的根文件夹中创建一个新文件夹models来保存所有模型。
在模型文件夹中创建一个新文件Catchphrase.go,其内容如下:
package models
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Catchphrase struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
MovieName string `json:"movieName,omitempty" bson:"movieName,omitempty"`
Catchphrase string `json:"catchphrase,omitempty" bson:"catchphrase,omitempty"`
MovieContext string `json:"movieContext,omitempty" bson:"movieContext,omitempty"`
}
进入全屏模式 退出全屏模式
创建流行语控制器
让我们在应用程序的根文件夹中创建一个新文件夹controllers来保存所有控制器。
在 controllers 文件夹中创建一个新文件catchphraseController.go,其内容如下:
package controllers
import (
"context"
"log"
"math"
"strconv"
"time"
"github.com/gofiber/fiber/v2"
"github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/config"
"github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
func GetAllCatchphrases(c *fiber.Ctx) error {
catchphraseCollection := config.MI.DB.Collection("catchphrases")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
var catchphrases []models.Catchphrase
filter := bson.M{}
findOptions := options.Find()
if s := c.Query("s"); s != "" {
filter = bson.M{
"$or": []bson.M{
{
"movieName": bson.M{
"$regex": primitive.Regex{
Pattern: s,
Options: "i",
},
},
},
{
"catchphrase": bson.M{
"$regex": primitive.Regex{
Pattern: s,
Options: "i",
},
},
},
},
}
}
page, _ := strconv.Atoi(c.Query("page", "1"))
limitVal, _ := strconv.Atoi(c.Query("limit", "10"))
var limit int64 = int64(limitVal)
total, _ := catchphraseCollection.CountDocuments(ctx, filter)
findOptions.SetSkip((int64(page) - 1) * limit)
findOptions.SetLimit(limit)
cursor, err := catchphraseCollection.Find(ctx, filter, findOptions)
defer cursor.Close(ctx)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Catchphrases Not found",
"error": err,
})
}
for cursor.Next(ctx) {
var catchphrase models.Catchphrase
cursor.Decode(&catchphrase)
catchphrases = append(catchphrases, catchphrase)
}
last := math.Ceil(float64(total / limit))
if last < 1 && total > 0 {
last = 1
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"data": catchphrases,
"total": total,
"page": page,
"last_page": last,
"limit": limit,
})
}
func GetCatchphrase(c *fiber.Ctx) error {
catchphraseCollection := config.MI.DB.Collection("catchphrases")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
var catchphrase models.Catchphrase
objId, err := primitive.ObjectIDFromHex(c.Params("id"))
findResult := catchphraseCollection.FindOne(ctx, bson.M{"_id": objId})
if err := findResult.Err(); err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Catchphrase Not found",
"error": err,
})
}
err = findResult.Decode(&catchphrase)
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Catchphrase Not found",
"error": err,
})
}
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"data": catchphrase,
"success": true,
})
}
func AddCatchphrase(c *fiber.Ctx) error {
catchphraseCollection := config.MI.DB.Collection("catchphrases")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
catchphrase := new(models.Catchphrase)
if err := c.BodyParser(catchphrase); err != nil {
log.Println(err)
return c.Status(400).JSON(fiber.Map{
"success": false,
"message": "Failed to parse body",
"error": err,
})
}
result, err := catchphraseCollection.InsertOne(ctx, catchphrase)
if err != nil {
return c.Status(500).JSON(fiber.Map{
"success": false,
"message": "Catchphrase failed to insert",
"error": err,
})
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"data": result,
"success": true,
"message": "Catchphrase inserted successfully",
})
}
func UpdateCatchphrase(c *fiber.Ctx) error {
catchphraseCollection := config.MI.DB.Collection("catchphrases")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
catchphrase := new(models.Catchphrase)
if err := c.BodyParser(catchphrase); err != nil {
log.Println(err)
return c.Status(400).JSON(fiber.Map{
"success": false,
"message": "Failed to parse body",
"error": err,
})
}
objId, err := primitive.ObjectIDFromHex(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Catchphrase not found",
"error": err,
})
}
update := bson.M{
"$set": catchphrase,
}
_, err = catchphraseCollection.UpdateOne(ctx, bson.M{"_id": objId}, update)
if err != nil {
return c.Status(500).JSON(fiber.Map{
"success": false,
"message": "Catchphrase failed to update",
"error": err.Error(),
})
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"success": true,
"message": "Catchphrase updated successfully",
})
}
func DeleteCatchphrase(c *fiber.Ctx) error {
catchphraseCollection := config.MI.DB.Collection("catchphrases")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
objId, err := primitive.ObjectIDFromHex(c.Params("id"))
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"message": "Catchphrase not found",
"error": err,
})
}
_, err = catchphraseCollection.DeleteOne(ctx, bson.M{"_id": objId})
if err != nil {
return c.Status(500).JSON(fiber.Map{
"success": false,
"message": "Catchphrase failed to delete",
"error": err,
})
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"success": true,
"message": "Catchphrase deleted successfully",
})
}
进入全屏模式 退出全屏模式
控制器文件将包含用于查询我们的数据库的逻辑。
创建流行语路线
让我们在应用程序的根文件夹中创建一个新文件夹routes来保存所有路由。
在 routes 文件夹中创建一个新文件catchphrases.go,其内容如下:
package routes
import (
"github.com/gofiber/fiber/v2"
"github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/controllers" // replace
)
func CatchphrasesRoute(route fiber.Router) {
route.Get("/", controllers.GetAllCatchphrases)
route.Get("/:id", controllers.GetCatchphrase)
route.Post("/", controllers.AddCatchphrase)
route.Put("/:id", controllers.UpdateCatchphrase)
route.Delete("/:id", controllers.DeleteCatchphrase)
}
进入全屏模式 退出全屏模式
放在一起
修改main.go文件如下:
package main
import (
"log"
"os"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/joho/godotenv"
"github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/config"
"github.com/mikefmeyer/catchphrase-go-mongodb-rest-api/routes"
)
func setupRoutes(app *fiber.App) {
app.Get("/", func(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(fiber.Map{
"success": true,
"message": "You are at the root endpoint 😉",
"github_repo": "https://github.com/MikeFMeyer/catchphrase-go-mongodb-rest-api",
})
})
api := app.Group("/api")
routes.CatchphrasesRoute(api.Group("/catchphrases"))
}
func main() {
if os.Getenv("APP_ENV") != "production" {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
}
app := fiber.New()
app.Use(cors.New())
app.Use(logger.New())
config.ConnectDB()
setupRoutes(app)
port := os.Getenv("PORT")
err := app.Listen(":" + port)
if err != nil {
log.Fatal("Error app failed to start")
panic(err)
}
}
进入全屏模式 退出全屏模式
运行应用程序后,您应该能够导航到以下路线localhost:3000/api/catchphrases以查看数据库中的流行语。
在 Heroku 上托管
Heroku 允许您免费托管您的应用程序,但资源有限。要设置项目,请使用Heroku 官方文档中的以下网页。
注意:您可能需要添加以下配置变量才能运行应用程序:
MONGO_URI = <Your mongo uri>
DB = <Your database name>
PORT = 8080
APP_ENV = production
进入全屏模式 退出全屏模式
额外
这是我用于此 API 的数据集。
电影流行语数据集
感谢阅读
这是使用 Fiber (Go) 和 MongoDB 构建的 REST API 的一个非常基本的示例。代码可以从github下载。
更多推荐
所有评论(0)