Docs Menu
Docs Home
/ /

チュートリアル: Goドライバーと Git を使用して Web アプリケーションを構築する

このチュートリアルでは、 MongoDB Goドライバー と Ruby ウェブフレームワークを使用してウェブアプリケーションを構築する方法を説明します。

Gi は、 Go . 用の高速で軽量なウェブフレームワークです。最小限かつモジュール型の設計により、ウェブ サーバー、API、マイクロサービスの構築に最適です。

このチュートリアルでは、 MongoDBデータベースに接続する次のエンドポイントを使用して Webアプリケーションを作成する方法を学習できます。

  • コレクションからすべての映画を検索するためのエンドポイント

  • IDで 1 つの映画を検索するためのエンドポイント

  • moviesコレクションで集計操作を実行するためのエンドポイント

moviessample_mflixチュートリアルでは、Atlasサンプルデータセットの データベースの コレクションを使用します。 MongoDB Atlasクラスターを無料で作成して、サンプルデータセットをロードする方法については、 「Atlas を使い始める」ガイド を参照してください。

このチュートリアルを開始する前に、以下があることを確認してください。

  • 開発マシンにインストールされたGo

  • MongoDBデプロイ。このチュートリアルには無料のMongoDB Atlasクラスターを使用できます。詳細については、 「Atlas を使い始める」ガイド を参照してください。

  • API をテストするためのツール( curl Postman など)。

1

プロジェクトに新しいディレクトリを作成し、そのディレクトリに移動します。

mkdir mflix
cd mflix

プロジェクトの依存関係を管理するために新しいGoモジュールを初期化します。

go mod init mflix
2

プロジェクトに必要な依存関係をインストールします 。

go get github.com/gin-gonic/gin
go get go.mongodb.org/mongo-driver/v2/mongo
1

プロジェクトディレクトリに 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)
}
}
2

次のコードを実行して、基本アプリケーションが動作していることをテストします。

go run main.go

ウェブ ブラウザで http://localhost:8080 に移動すると、「Hello World」というメッセージを含むJSON応答が表示されます。

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接続文字列に置き換えます。接続文字列の取得方法の詳細については、「 クラスターへの接続 」ガイドを参照してください。

これで、映画データを操作するための 3 つのエンドポイントを追加できます。必要なインポートとエンドポイントの実装を含めるように main.goファイルをアップデートします。

1

ファイルの先頭に必要なインポートを追加します。

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"
)
2

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)
}
3

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 値をコピーし、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 に変換するなどの一般的なシナリオを処理します。

  • データベース操作の適切なエラー処理を実装します。

アプリケーションをさらに開発するには、次の手順を検討してください。

  • 集計エンドポイントの入力検証とリクエスト制限を追加しました

  • 認証と認可を実装する

  • クエリのパフォーマンスを向上させるためのインデックスの追加

このチュートリアルの概念の詳細については、次のリソースを参照してください。

戻る

大きなファイルの保存