Docs Menu
Docs Home
/ /

Tutorial: Compile una aplicación web con el driver de Go y Gin.

Este tutorial le muestra cómo crear una aplicación web utilizando el controlador MongoDB Go y el Marco web Gin.

Gin es un framework web rápido y ligero para Go. Su diseño minimalista y modular lo hace ideal para crear servidores web, API y microservicios.

En este tutorial, puede aprender a crear una aplicación web con los siguientes puntos finales que se conectan a una base de datos MongoDB:

  • Un punto final para recuperar todas las películas de una colección

  • Un endpoint para recuperar una sola película por ID

  • Un punto final para ejecutar operaciones de agregación en el movies Colección

El tutorial utiliza la movies colección sample_mflix de la base de datos de los conjuntos de datos de ejemplo de Atlas. Para aprender a crear un clúster gratuito de MongoDB Atlas y cargar los conjuntos de datos de ejemplo, consulte la guía "Comenzar con Atlas".

Antes de comenzar este tutorial, asegúrese de tener lo siguiente:

  • Vaya instalado en su máquina de desarrollo

  • Una implementación de MongoDB. Puedes utilizar un clúster gratuito de MongoDB Atlas para este tutorial. Para obtener más información, consulta la guía Comienza con Atlas.

  • Una herramienta para probar su API, como curl o Postman.

1

Crea un nuevo directorio para tu proyecto y navega hacia él:

mkdir mflix
cd mflix

Inicialice un nuevo módulo Go para administrar las dependencias de su proyecto:

go mod init mflix
2

Instale las dependencias necesarias para su proyecto:

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

Cree un archivo main.go en el directorio de su proyecto y agregue el siguiente código para configurar una aplicación Gin básica:

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

Ejecute el siguiente código para probar que su aplicación básica está funcionando:

go run main.go

Navegue a http://localhost:8080 en su navegador web para ver una respuesta JSON con el mensaje "Hola mundo".

Actualice su archivo main.go para conectarse a MongoDB. Reemplace el código existente con el siguiente para conectarse a su implementación de 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)
}
}

Reemplace el <connection-string> marcador con su cadena de conexión de MongoDB. Para obtener más información sobre cómo obtener su cadena de conexión, consulte la guía "Conectarse a un clúster".

Ahora puedes agregar tres puntos finales para interactuar con los datos de la película. Actualiza tu archivo main.go para incluir las importaciones y las implementaciones de puntos finales necesarias.

1

Agregue las importaciones necesarias en la parte superior de su archivo:

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

Agregue los siguientes controladores de ruta después de su función 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

Actualice su función main para registrar las nuevas rutas:

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

Puede probar los puntos finales que creó para asegurarse de que funcionen como se espera.

Reinicie su servidor y navegue a http://localhost:8080/movies en su navegador web. Debería ver una respuesta JSON con una matriz de películas del conjunto de datos de muestra.

Para probar el punto final de película única, copie un valor _id de la respuesta de películas y navegue hasta http://localhost:8080/movies/{id}.

Por ejemplo, si navega a http://localhost:8080/movies/573a1390f29313caabcd42e8, la respuesta en el navegador es similar a la siguiente:

{
"_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"
],"
....
}

Para probar el punto final de agregación, debe realizar una solicitud POST. Puede usar una herramienta como curl o Postman para enviar una solicitud con una canalización de agregación en el cuerpo de la solicitud.

El siguiente ejemplo de agregación cuenta las películas de comedia por año:

[
{"$match": {"genres": "Comedy"}},
{"$group": {
"_id": "$year",
"count": {"$sum": 1}
}},
{"$sort": {"count": -1}}
]

Ejecute el siguiente código en su terminal para probar este punto final con 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
},
...
]

Advertencia

El punto final de agregación de este tutorial permite a los usuarios ejecutar cualquier canalización de agregación. En un entorno de producción, se recomienda limitar los tipos de operaciones y validar la entrada para evitar posibles problemas de seguridad o de rendimiento.

El siguiente archivo muestra el código completo para su aplicación 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)
}

Ahora tienes una aplicación básica que demuestra lo siguiente:

  • Configura un proyecto Go con el marco web Gin y el controlador Go.

  • Se conecta a una base de datos MongoDB mediante el controlador Go.

  • Implementa puntos finales de API REST que realizan operaciones MongoDB para buscar documentos, buscar un solo documento y ejecutar canales de agregación.

  • Maneja escenarios comunes como la conversión de ID de cadenas a ID de objetos.

  • Implementa el manejo adecuado de errores para las operaciones de base de datos.

Para desarrollar aún más su aplicación, considere los siguientes pasos:

  • Agregue validación de entrada y limitación de solicitudes para el punto final de agregación

  • Implementar autenticación y autorización

  • Agregar índices para mejorar el rendimiento de las consultas

Para aprender más sobre los conceptos de este tutorial, consulta los siguientes recursos:

Volver

Almacene archivos grandes