Visão geral
Este tutorial mostra como criar um aplicativo web usando o MongoDB Go driver e o Gin web framework.
O Gi é um framework web rápido e leve para Go. Seu design mínima e modular o torna ideal para construir servidores web, APIs e microsserviços.
Neste tutorial, você pode aprender como criar um aplicativo da web com os seguintes pontos de extremidade que se conectam a um banco de dados MongoDB:
Um ponto de extremidade para recuperar todos os filmes de uma coleção
Um ponto de extremidade para recuperar um único filme por ID
Um ponto de extremidade para executar operações de agregação na coleção
movies
Dados de amostra
O tutorial utiliza a coleção movies no banco de dados sample_mflix a partir do conjunto de dados de amostra do Atlas. Para aprender como criar um cluster MongoDB Atlas gratuito e carregar os conjuntos de dados de amostra, consulte o Get Started with Atlas guia.
Pré-requisitos
Antes de iniciar este tutorial, verifique se você tem o seguinte:
Go instalado em sua máquina de desenvolvimento
Uma implantação MongoDB. Você pode utilizar um cluster MongoDB Atlas gratuito para este tutorial. Para mais informações, consulte o guia Iniciar com Atlas.
Configurar seu projeto
Criar a Estrutura do Aplicativo
Criar o arquivo do aplicativo principal
Crie um arquivo main.go no diretório do projeto e adicione o seguinte código para configurar um aplicativo Gin básico:
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) } }
Conecte-se ao MongoDB
Atualize seu arquivo main.go para se conectar ao MongoDB. Substitua o código existente pelo seguinte código para se conectar à sua implantação do 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) } }
Substitua o espaço reservado <connection-string> por sua string de conexão real do MongoDB . Para saber mais sobre como obter sua string de conexão, consulte o Conectar a um cluster guia.
Implemente os pontos de extremidade da API
Agora, você pode adicionar três pontos de extremidade para interação com os dados do filme. Atualize seu arquivo main.go para incluir as importações e implementações de pontos de extremidade necessárias.
Adicionar manipuladores de rota
Adicione os seguintes manipuladores de rota após sua função 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) }
Registrar rotas
Atualize sua função main para registrar as novas rotas:
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) } }
Teste seu aplicativo
Você pode testar os pontos de extremidade criados para garantir que funcionem conforme o esperado.
Teste Obter Todos os filmes
Reinicie seu servidor e navegue até http://localhost:8080/movies em seu navegador da web. Você deverá ver uma resposta JSON contendo uma array de filmes do conjunto de dados de amostra.
Teste Obter filme por ID
Para testar o ponto de extremidade de filme único, copie um valor _id da resposta do filme e navegue até http://localhost:8080/movies/{id}.
Por exemplo, se você navegar para http://localhost:8080/movies/573a1390f29313caabcd42e8, a resposta no navegador será semelhante ao seguinte:
{ "_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" ]," .... }
Ponto de extremidade de agregação de teste
Para testar o ponto de extremidade de agregação, é necessário fazer uma solicitação publicação. Você pode usar uma ferramenta como curl ou Postman para enviar uma solicitação com um pipeline de agregação no corpo da solicitação.
O exemplo de agregação a seguir conta filmes de comedia por ano:
[ {"$match": {"genres": "Comedy"}}, {"$group": { "_id": "$year", "count": {"$sum": 1} }}, {"$sort": {"count": -1}} ]
Execute o seguinte código no seu terminal para testar esse ponto de extremidade com 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 }, ... ]
Aviso
O ponto de extremidade de agregação neste tutorial permite que os usuários executem qualquer pipeline de agregação. Em um ambiente de produção, você deve limitar os tipos de operações e validar a entrada para evitar possíveis problemas de segurança ou de desempenho.
Código completo
O seguinte arquivo mostra o código completo do seu aplicativo da 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) }
Resumo
Agora você tem um aplicativo básico que demonstra o seguinte:
Configura um projeto Go com o framework web Gi e o driver Go.
Conecta-se a um banco de dados MongoDB usando o driver Go.
Implementa pontos de extremidade de API REST que executam operações MongoDB para localizar documentos, localizar um único documento e executar pipelines de agregação.
Lida com cenários comuns, como converter IDs de string em ObjectIDs.
Implementa o tratamento adequado de erros para operações de banco de dados .
Próximos passos
Para desenvolver ainda mais seu aplicativo, considere as seguintes etapas a seguir:
Adicionar validação de entrada e limitação de solicitação para o ponto de extremidade de agregação
Implementar autenticação e autorização
Adicionar índices para melhorar o desempenho da query
Recursos adicionais
Para saber mais sobre os conceitos deste tutorial, consulte os seguintes recursos: