Overview
このチュートリアルでは、 MongoDB Goドライバー と Ruby ウェブフレームワークを使用してウェブアプリケーションを構築する方法を説明します。
Gi は、 Go . 用の高速で軽量なウェブフレームワークです。最小限かつモジュール型の設計により、ウェブ サーバー、API、マイクロサービスの構築に最適です。
このチュートリアルでは、 MongoDBデータベースに接続する次のエンドポイントを使用して Webアプリケーションを作成する方法を学習できます。
コレクションからすべての映画を検索するためのエンドポイント
IDで 1 つの映画を検索するためのエンドポイント
moviesコレクションで集計操作を実行するためのエンドポイント
サンプル データ
moviessample_mflixチュートリアルでは、Atlasサンプルデータセットの データベースの コレクションを使用します。 MongoDB Atlasクラスターを無料で作成して、サンプルデータセットをロードする方法については、 「Atlas を使い始める」ガイド を参照してください。
前提条件
このチュートリアルを開始する前に、以下があることを確認してください。
MongoDBデプロイ。このチュートリアルには無料のMongoDB Atlasクラスターを使用できます。詳細については、 「Atlas を使い始める」ガイド を参照してください。
API をテストするためのツール( curl や Postman など)。
プロジェクトを設定する
アプリケーション構造の作成
メインのアプリケーションファイルを作成する
プロジェクトディレクトリに main.goファイルを作成し、次のコードを追加して、基本的な Gitアプリケーションを設定します。
package main import ( "log" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } }
MongoDB に接続する
MongoDBに接続するには、main.goファイルを更新します。 MongoDBデプロイに接続するには、既存のコードを次のコードに置き換えます。
package main import ( "context" "log" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // Replace with your MongoDB connection string const uri = "<connection-string>" func main() { // Connects to MongoDB serverAPI := options.ServerAPI(options.ServerAPIVersion1) opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI) client, err := mongo.Connect(opts) if err != nil { log.Fatal("Could not connect to MongoDB:", err) } // Ensures the client disconnects when main exits defer func() { if err := client.Disconnect(context.TODO()); err != nil { log.Fatal("Error disconnecting from MongoDB:", err) } }() // Pings the database to verify connection if err := client.Ping(context.TODO(), nil); err != nil { log.Fatal("Could not ping MongoDB:", err) } r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } }
<connection-string>プレースホルダーを実際のMongoDB接続文字列に置き換えます。接続文字列の取得方法の詳細については、「 クラスターへの接続 」ガイドを参照してください。
APIエンドポイントの実装
これで、映画データを操作するための 3 つのエンドポイントを追加できます。必要なインポートとエンドポイントの実装を含めるように main.goファイルをアップデートします。
ルート ハンドラーを追加する
connectToMongoDB 関数の後に次のルート ハンドラーを追加します。
// GET /movies - Retrieves all movies func getMovies(c *gin.Context, client *mongo.Client) { // Find all movies in the collection cursor, err := client.Database("sample_mflix").Collection("movies").Find(c.Request.Context(), bson.D{}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes all results var movies []bson.D if err = cursor.All(c.Request.Context(), &movies); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movies c.JSON(http.StatusOK, movies) } // GET /movies/:id - Retrieves a movie by ID func getMovieByID(c *gin.Context, client *mongo.Client) { // Gets the movie ID from the URL parameter as a string idStr := c.Param("id") // Converts string ID to MongoDB ObjectID id, err := bson.ObjectIDFromHex(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid movie ID format"}) return } // Finds the movie by ObjectID var movie bson.D err = client.Database("sample_mflix").Collection("movies").FindOne(c.Request.Context(), bson.D{{"_id", id}}).Decode(&movie) if err != nil { if err == mongo.ErrNoDocuments { c.JSON(http.StatusNotFound, gin.H{"error": "Movie not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movie c.JSON(http.StatusOK, movie) } // POST /movies/aggregations - Runs aggregation pipeline func aggregateMovies(c *gin.Context, client *mongo.Client) { // Gets aggregation pipeline from request body var pipeline interface{} if err := c.ShouldBindJSON(&pipeline); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid aggregation pipeline"}) return } // Executes the aggregation pipeline cursor, err := client.Database("sample_mflix").Collection("movies").Aggregate(c.Request.Context(), pipeline) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes the results var result []bson.D if err = cursor.All(c.Request.Context(), &result); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the aggregation result c.JSON(http.StatusOK, result) }
ルートを登録する
main 関数を更新して新しいルートを登録します。
func main() { // Connects to MongoDB serverAPI := options.ServerAPI(options.ServerAPIVersion1) opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI) client, err := mongo.Connect(opts) if err != nil { log.Fatal("Could not connect to MongoDB:", err) } // Ensures the client disconnects when main exits defer func() { if err := client.Disconnect(context.TODO()); err != nil { log.Fatal("Error disconnecting from MongoDB:", err) } }() // Pings the database to verify connection if err := client.Ping(context.TODO(), nil); err != nil { log.Fatal("Could not ping MongoDB:", err) } r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) // Registers movie endpoints r.GET("/movies", func(c *gin.Context) { getMovies(c, client) }) r.GET("/movies/:id", func(c *gin.Context) { getMovieByID(c, client) }) r.POST("/movies/aggregations", func(c *gin.Context) { aggregateMovies(c, client) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } }
アプリケーションをテストする
作成したエンドポイントが期待どおりに動作することを確認できます。
すべての映画を取得するテスト
サーバーを再起動し、ウェブ ブラウザで http://localhost:8080/movies に移動します。サンプルデータセットからの映画の配列を含むJSON応答が表示されます。
IDで映画を取得するテスト
単一の映画エンドポイントをテストするには、映画のレスポンスから _id 値をコピーし、http://localhost:8080/movies/{id} に移動します。
例、http://localhost:8080/movies/573a1390f29313caabcd42e8 に移動すると、ブラウザの応答は次のようになります。
{ "_id": "573a1390f29313caabcd42e8", "awards": { "wins": 1, "nominations": 0, "text": "1 win." }, "cast": [ "A.C. Abadie", "Gilbert M. 'Broncho Billy' Anderson", "George Barnes", "Justus D. Barnes" ], "countries": [ "USA" ], "directors": [ "Edwin S. Porter" ], "fullplot": "Among the earliest existing films in American cinema - notable as the first film that presented a narrative story to tell - it depicts a group of cowboy outlaws who hold up a train and rob the passengers. They are then pursued by a Sheriff's posse. Several scenes have color included - all hand tinted.", "genres": [ "Short", "Western" ]," .... }
集計エンドポイントのテスト
集計エンドポイントをテストするには、 POSTリクエストを行う必要があります。 curl や Postman などのツールを使用して、リクエスト本文に集計パイプラインを含むリクエストを送信できます。
次の集計の例では、Community 映画のカウントを年ごとにカウントしています。
[ {"$match": {"genres": "Comedy"}}, {"$group": { "_id": "$year", "count": {"$sum": 1} }}, {"$sort": {"count": -1}} ]
ターミナルで次のコードを実行して、このエンドポイントを curl でテストします。
curl -X POST http://localhost:8080/movies/aggregations \ -H "Content-Type: application/json" \ -d '[ {"$match": {"genres": "Comedy"}}, {"$group": {"_id": "$year", "count": {"$sum": 1}}}, {"$sort": {"count": -1}} ]'
[ { "_id": 2014, "count": 287 }, { "_id": 2013, "count": 286 }, { "_id": 2009, "count": 268 }, { "_id": 2011, "count": 263 }, { "_id": 2006, "count": 260 }, ... ]
警告
このチュートリアルの集計エンドポイントを使用すると、ユーザーは任意の集計パイプラインを実行できます。実稼働環境では、潜在的なセキュリティの問題やパフォーマンスの問題を防ぐために、操作の種類を制限し、入力を検証する必要があります。
完全なコード
次のファイルは、Webアプリケーションの完全なコードを示しています。
package main import ( "context" "log" "net/http" "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) // Replace with your MongoDB connection string const uri = "YOUR-CONNECTION-STRING-HERE" func main() { // Connects to MongoDB serverAPI := options.ServerAPI(options.ServerAPIVersion1) opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI) client, err := mongo.Connect(opts) if err != nil { log.Fatal("Could not connect to MongoDB:", err) } // Ensures the client disconnects when main exits defer func() { if err := client.Disconnect(context.TODO()); err != nil { log.Fatal("Error disconnecting from MongoDB:", err) } }() // Pings the database to verify connection if err := client.Ping(context.TODO(), nil); err != nil { log.Fatal("Could not ping MongoDB:", err) } r := gin.Default() r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello World", }) }) // Registers movie endpoints r.GET("/movies", func(c *gin.Context) { getMovies(c, client) }) r.GET("/movies/:id", func(c *gin.Context) { getMovieByID(c, client) }) r.POST("/movies/aggregations", func(c *gin.Context) { aggregateMovies(c, client) }) if err := r.Run(); err != nil { log.Fatal("Failed to start server:", err) } } // GET /movies - Retrieves all movies func getMovies(c *gin.Context, client *mongo.Client) { // Find all movies in the collection cursor, err := client.Database("sample_mflix").Collection("movies").Find(c.Request.Context(), bson.D{}) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes all results var movies []bson.D if err = cursor.All(c.Request.Context(), &movies); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movies c.JSON(http.StatusOK, movies) } // GET /movies/:id - Retrieves a movie by ID func getMovieByID(c *gin.Context, client *mongo.Client) { // Gets the movie ID from the URL parameter as a string idStr := c.Param("id") // Converts string ID to MongoDB ObjectID id, err := bson.ObjectIDFromHex(idStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid movie ID format"}) return } // Finds the movie by ObjectID var movie bson.D err = client.Database("sample_mflix").Collection("movies").FindOne(c.Request.Context(), bson.D{{"_id", id}}).Decode(&movie) if err != nil { if err == mongo.ErrNoDocuments { c.JSON(http.StatusNotFound, gin.H{"error": "Movie not found"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the movie c.JSON(http.StatusOK, movie) } // POST /movies/aggregations - Runs aggregation pipeline func aggregateMovies(c *gin.Context, client *mongo.Client) { // Gets aggregation pipeline from request body var pipeline interface{} if err := c.ShouldBindJSON(&pipeline); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid aggregation pipeline"}) return } // Executes the aggregation pipeline cursor, err := client.Database("sample_mflix").Collection("movies").Aggregate(c.Request.Context(), pipeline) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } defer cursor.Close(c.Request.Context()) // Decodes the results var result []bson.D if err = cursor.All(c.Request.Context(), &result); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Returns the aggregation result c.JSON(http.StatusOK, result) }
概要
これで、次の内容を表示する基本的なアプリケーションができました。
Git WebフレームワークとGoドライバーを使用してGoプロジェクトを設定します。
Goドライバーを使用してMongoDBデータベースに接続します。
MongoDB操作を実行してドキュメントを検索し、単一のドキュメントを検索し、集計パイプラインを実行するREST APIエンドポイントを実装します。
string ID を ObjectID に変換するなどの一般的なシナリオを処理します。
データベース操作の適切なエラー処理を実装します。
次のステップ
アプリケーションをさらに開発するには、次の手順を検討してください。
集計エンドポイントの入力検証とリクエスト制限を追加しました
追加リソース
このチュートリアルの概念の詳細については、次のリソースを参照してください。