Últimas entradas

Creación de REST API con Go y MySQL

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:

Agregue un comentario

Su dirección de correo no se hará público. Los campos requeridos están marcados *