Go es un lenguaje creado por Google allá por el 2009, y se está extendiendo como una nueva alternativa a otros lenguajes ya asentados como Java, Python o C/C++ entre otros.
Si bien existen bastantes frameworks para crear APIs en la mayoría de los casos no es necesario, cuando recurrimos a Go para realizar un proyecto es porque es más ligero y rápido que otros lenguajes actuales.
Creación de objetos de base de datos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# Creación de base de datos CREATE DATABASE `northwind` # Creación de tabla CREATE TABLE IF NOT EXISTS `northwind`.`products` ( `supplier_ids` LONGTEXT NULL DEFAULT NULL, `id` INT(11) NOT NULL AUTO_INCREMENT, `product_code` VARCHAR(25) NULL DEFAULT NULL, `product_name` VARCHAR(50) NULL DEFAULT NULL, `description` LONGTEXT NULL DEFAULT NULL, `standard_cost` DECIMAL(19,4) NULL DEFAULT '0.0000', `list_price` DECIMAL(19,4) NOT NULL DEFAULT '0.0000', `reorder_level` INT(11) NULL DEFAULT NULL, `target_level` INT(11) NULL DEFAULT NULL, `quantity_per_unit` VARCHAR(50) NULL DEFAULT NULL, `discontinued` TINYINT(1) NOT NULL DEFAULT '0', `minimum_reorder_quantity` INT(11) NULL DEFAULT NULL, `category` VARCHAR(50) NULL DEFAULT NULL, `attachments` LONGBLOB NULL DEFAULT NULL, PRIMARY KEY (`id`), INDEX `product_code` (`product_code` ASC)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; # Inserción de datos INSERT INTO `products` (`supplier_ids`, `id`, `product_code`, `product_name`, `description`, `standard_cost`, `list_price`, `reorder_level`, `target_level`, `quantity_per_unit`, `discontinued`, `minimum_reorder_quantity`, `category`, `attachments`) VALUES ('4', 1, 'NWTB-1', 'Northwind Traders Chai', NULL, 13.5, 18, 10, 40, '10 boxes x 20 bags', 0, 10, 'Beverages', ''); INSERT INTO `products` (`supplier_ids`, `id`, `product_code`, `product_name`, `description`, `standard_cost`, `list_price`, `reorder_level`, `target_level`, `quantity_per_unit`, `discontinued`, `minimum_reorder_quantity`, `category`, `attachments`) VALUES ('10', 3, 'NWTCO-3', 'Northwind Traders Syrup', NULL, 7.5, 10, 25, 100, '12 - 550 ml bottles', 0, 25, 'Condiments', ''); INSERT INTO `products` (`supplier_ids`, `id`, `product_code`, `product_name`, `description`, `standard_cost`, `list_price`, `reorder_level`, `target_level`, `quantity_per_unit`, `discontinued`, `minimum_reorder_quantity`, `category`, `attachments`) VALUES ('10', 4, 'NWTCO-4', 'Northwind Traders Cajun Seasoning', NULL, 16.5, 22, 10, 40, '48 - 6 oz jars', 0, 10, 'Condiments', ''); |
Conexión a la base de datos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package database import "database/sql" func InitDB() *sql.DB { connectionString := "usuario:contreña@tcp(localhost:3306)/northwind" databaseConnection, err := sql.Open("mysql", connectionString) if err != nil { panic(err.Error()) // Error Handling = manejo de errores } return databaseConnection } |
Definimos el paquete database e importamos la dependencia “database/sql” para la comunicación con la base de datos MySQL, en la función InitDB() definimos los parámetros de conexión a la base de datos.
Mas información de los drivers de conexiones a bases de datos SQL en el siguiente enlace https://golang.org/pkg/database/sql/
1 2 3 4 5 6 7 8 9 10 11 |
package main import ( "api-rest-go/database" "database/sql" "encoding/json" "net/http" "github.com/go-chi/chi" _ "github.com/go-sql-driver/mysql" ) |
- “api-rest-go/database”: Carpeta donde se encuentra la conexion a la base de datos mysql
- “database/sql”: Paquete sql, proporciona una interfaz genérica para bases de datos SQL
- “encoding/json”: Paquete json que implementa la codificación y decodificación de JSON
- “net/http”: Paquete http, proporciona implementaciones de servidor y cliente HTTP.
- “github.com/go-chi/chi”: Enrutador ligero para construir servicios Go via HTTP
- _ “github.com/go-sql-driver/mysql”: Controlador MySQL para el paquete de base de datos sql de Go
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var databaseConnection *sql.DB type Product struct { ID int `json:"id"` Product_Code string `json:"product_code"` Description string `json:"description"` } func catch(err error) { if err != nil { panic(err) } } |
Se define una variable para la comunicación con el driver de base de datos y se define un tipo de dato types.
Con la función catch definimos el manejo de errores, será reutilizada por el resto de funciones
1 2 3 4 5 6 7 8 9 10 11 12 |
func main() { databaseConnection = database.InitDB() defer databaseConnection.Close() r := chi.NewRouter() r.Get("/products", AllProductos) r.Post("/products", CreateProducto) r.Put("/products/{id}", UpdateProducto) r.Delete("/products/{id}", DeleteProducto) http.ListenAndServe(":3000", r) } |
En la función main definimos las rutas de nuestra API REST
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
func AllProductos(w http.ResponseWriter, r *http.Request) { const sql = `SELECT id,product_code,COALESCE(description,'') FROM products` results, err := databaseConnection.Query(sql) catch(err) var products []*Product for results.Next() { product := &Product{} err = results.Scan(&product.ID, &product.Product_Code, &product.Description) catch(err) products = append(products, product) } respondwithJSON(w, http.StatusOK, products) } |
Con la función AllProductos se devolverá una la colección de productos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func CreateProducto(w http.ResponseWriter, r *http.Request) { var producto Product json.NewDecoder(r.Body).Decode(&producto) query, err := databaseConnection.Prepare("Insert products SET product_code=?, description=?") catch(err) _, er := query.Exec(producto.Product_Code, producto.Description) catch(er) defer query.Close() respondwithJSON(w, http.StatusCreated, map[string]string{"message": "successfully created"}) } |
La función CreateProducto permitirá, a traves del verbo POST, crear un nuevo producto.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func UpdateProducto(w http.ResponseWriter, r *http.Request) { var product Product id := chi.URLParam(r, "id") json.NewDecoder(r.Body).Decode(&product) query, err := databaseConnection.Prepare("Update products set product_code=?, description=? where id=?") catch(err) _, er := query.Exec(product.Product_Code, product.Description, id) catch(er) defer query.Close() respondwithJSON(w, http.StatusOK, map[string]string{"message": "update successfully"}) } |
Con la función UpdateProducto, permitirá actualizar un producto.
1 2 3 4 5 6 7 8 9 10 11 12 |
func DeleteProducto(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") query, err := databaseConnection.Prepare("delete from products where id=?") catch(err) _, er := query.Exec(id) catch(er) query.Close() respondwithJSON(w, http.StatusOK, map[string]string{"message": "successfully deleted"}) } |
La función DeleteProducto permitirá eliminar un producto.
1 2 3 4 5 6 7 |
func respondwithJSON(w http.ResponseWriter, code int, payload interface{}) { response, _ := json.Marshal(payload) w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) w.Write(response) } |
La función respondwithJSON permite devolver las respuestas en formato Json.
Mas detalle en el repositorio https://github.com/jorgedison/api-rest-go
En un próximo post se utilizará un framework donde se haría mención a conceptos como models, services, repository.
Fuente: