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
# 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
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/
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
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
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
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.
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.
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.
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.
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: