📰 IT NachrichtenAnthropic C.E.O. Dario Amodei Calls for A.I. Slowdown(12.09.2026 um 18:03 Uhr)
🔧 AI Nachrichten Etzioni on AI: What kids tell chatbots, but not you(04.09.2026 um 16:05 Uhr)
📰 IT NachrichtenAnthropic C.E.O. Dario Amodei Calls for A.I. Slowdown(12.09.2026 um 18:03 Uhr)
🔧 AI Nachrichten Etzioni on AI: What kids tell chatbots, but not you(04.09.2026 um 16:05 Uhr)

🔧 Programmierung 🕛 vor 1 Jahr 23 Min Lesezeit
0

Developing a Simple RESTful API with Gin, ginvalidator, and validatorgo

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

This tutorial introduces the basics of writing a RESTful web service API with . Which uses my two open source libraries: .





  • .


  • .



Please check out the above libraries and don’t forget to ⭐ star, 🔁 share, and 🍴 fork!






API endpoint design



You’ll build an API that provides access to a product inventory management system. So you’ll need to provide endpoints through which a HTTP client can create, read update and delete products.



When developing an API, you typically begin by designing the endpoints. Your API’s users will have more success if the endpoints are easy to understand.



Here are the endpoints you’ll create in this tutorial.





  • /products



    • GET - Get a list of all products, returned as JSON.


    • POST - Add a new product from request data and returned as JSON.







  • /products/:id



    • GET - Get a product by its id, returning the product data as JSON.


    • UPDATE - Get a product by its id, returning the newly updated data as JSON.


    • Delete - Deletes a product by its id.








To keep things simple for the tutorial, you’ll store data in memory. A more typical API would interact with a database. Note that storing data in memory means that the list of products will be lost each time you stop the server, then recreated when you start it.



Also all HTTP requests will be represented as .http code snippets. You can use any HTTP client, such as Postman, Insomnia, or the HTTP client in your IDE, to send your requests.






Writing the Code




  • Start by following the and . To learn more about field syntax extraction, refer to this section.



    If you prefer not to use the append method in the route registration handlers, you can break down the middleware validator handlers into smaller chunks and add them individually to the registration handler.



    Thanks for reading, and see you next time!






    Full Code






    CODE
    package main

    import (
    "fmt"
    "net/http"
    "regexp"
    "time"

    gv "github.com/bube054/ginvalidator"
    vgo "github.com/bube054/validatorgo"
    "github.com/gin-gonic/gin"
    )

    // Dimensions represents the physical dimensions of a product.
    type Dimensions struct {
    Length float64 `json:"length"` // Length of the product in centimeters
    Width float64 `json:"width"` // Width of the product in centimeters
    Height float64 `json:"height"` // Height of the product in centimeters
    Weight float64 `json:"weight"` // Weight of the product in kilograms
    }

    // Supplier represents information about the supplier of a product.
    type Supplier struct {
    Name string `json:"name"` // Name of the supplier
    Contact string `json:"contact"` // Contact details (e.g., email or phone number)
    Address string `json:"address"` // Address of the supplier
    }

    // Product represents data about an inventory product.
    type Product struct {
    ID string `json:"id"` // Unique identifier for the product
    Name string `json:"name"` // Name of the product
    Category string `json:"category"` // Category the product belongs to
    Description string `json:"description"` // Detailed description of the product
    Price float64 `json:"price"` // Price of the product in decimal format
    Stock int `json:"stock"` // Quantity of the product available in stock
    Dimensions Dimensions `json:"dimensions"` // Physical dimensions of the product
    Supplier Supplier `json:"supplier"` // Supplier details
    Tags []string `json:"tags"` // Tags for searching and filtering
    Image string `json:"image"` // URLs of product images
    ManufacturedAt time.Time `json:"manufacturedAt"` // Timestamp when the product was manufactured
    CreatedAt time.Time `json:"createdAt"` // Timestamp when the product was created
    UpdatedAt time.Time `json:"updatedAt"` // Timestamp when the product was last updated
    }

    var products = []Product{
    {
    ID: "p1",
    Name: "Wireless Mouse",
    Category: "Electronics",
    Description: "A high-precision wireless mouse with ergonomic design.",
    Price: 29.99,
    Stock: 150,
    Dimensions: Dimensions{
    Length: 11.5,
    Width: 6.0,
    Height: 3.5,
    Weight: 0.2,
    },
    Supplier: Supplier{
    Name: "Tech Supplies Inc.",
    Contact: "[email protected]",
    Address: "123 Tech Street, Silicon Valley, CA",
    },
    Tags: []string{"wireless", "mouse", "electronics", "accessories"},
    Image: "https://example.com/images/mouse1.jpg",
    ManufacturedAt: time.Now().AddDate(0, -30, 0),
    CreatedAt: time.Now().AddDate(0, -1, 0),
    UpdatedAt: time.Now().AddDate(0, -1, 0),
    },
    {
    ID: "p2",
    Name: "Gaming Keyboard",
    Category: "Electronics",
    Description: "Mechanical keyboard with customizable RGB lighting.",
    Price: 79.99,
    Stock: 75,
    Dimensions: Dimensions{
    Length: 45.0,
    Width: 15.0,
    Height: 3.8,
    Weight: 1.1,
    },
    Supplier: Supplier{
    Name: "Gaming Gear Ltd.",
    Contact: "[email protected]",
    Address: "456 Gaming Road, Austin, TX",
    },
    Tags: []string{"keyboard", "gaming", "RGB", "electronics"},
    Image: "https://example.com/images/keyboard2.jpg",
    ManufacturedAt: time.Now().AddDate(0, -20, 0),
    CreatedAt: time.Now().AddDate(0, -2, 0),
    UpdatedAt: time.Now().AddDate(0, -2, 0),
    },
    }

    func main() {
    router := gin.Default()
    router.GET("/products",
    append(productQueriesValidators(), getProducts)...,
    )
    router.GET("/products/:id",
    append(productParamIdValidators(), getProduct)...,
    )
    router.DELETE("/products/:id",
    append(productParamIdValidators(), deleteProduct)...,
    )
    router.POST("/products",
    append(productBodyValidators(), postProduct)...,
    )
    router.PUT("/products/:id",
    append(append(productParamIdValidators(), productBodyValidators()...), putProducts)...,
    )
    router.Run(":8080")
    }

    func productQueriesValidators() gin.HandlersChain {
    return gin.HandlersChain{
    gv.NewQuery("q", nil).Chain().Optional().Trim(" ").Not().Empty(nil).Validate(),
    gv.NewQuery("order", nil).Chain().Optional().Trim(" ").In([]string{"asc", "desc"}).Validate(),
    }
    }

    func productParamIdValidators() gin.HandlersChain {
    return gin.HandlersChain{gv.NewParam("id", nil).Chain().Trim(" ").Alphanumeric(nil).Validate()}
    }

    func productBodyValidators() gin.HandlersChain {
    var (
    minDesc uint = 5
    maxDesc uint = 100
    minStock int = 0
    validCategories []string = []string{"Electronics", "Apparels", "Groceries", "Home-Appliances"}
    )

    return gin.HandlersChain{
    gv.NewBody("name", nil).Chain().Not().Empty(nil).Validate(),
    gv.NewBody("category", nil).Chain().In(validCategories).Validate(),
    gv.NewBody("description", nil).Chain().Length(&vgo.IsLengthOpts{Min: uint(minDesc), Max: &maxDesc}).Validate(),
    gv.NewBody("price", nil).Chain().Decimal(&vgo.IsDecimalOpts{DecimalDigits: vgo.DecimalDigits{Min: 2}, ForceDecimal: false, Locale: "en-US"}).Validate(),
    gv.NewBody("stock", nil).Chain().Int(&vgo.IsIntOpts{Min: &minStock}).Validate(),
    gv.NewBody(`dimensions.length`, nil).Chain().Numeric(nil).Validate(),
    gv.NewBody("dimensions.width", nil).Chain().Numeric(nil).Validate(),
    gv.NewBody("dimensions.height", nil).Chain().Numeric(nil).Validate(),
    gv.NewBody("dimensions.weight", nil).Chain().Numeric(nil).Validate(),
    gv.NewBody("supplier.name", nil).Chain().Trim(" ").Not().Empty(nil).Validate(),
    gv.NewBody("supplier.contact", nil).Chain().Email(nil).Validate(),
    gv.NewBody("supplier.address", nil).Chain().Matches(regexp.MustCompile(`^\d+\s[\w\s]+,\s[\w\s]+,\s[A-Z]{2}$`)).Validate(),
    gv.NewBody("tags", nil).Chain().Array(nil).Validate(),
    gv.NewBody("image", nil).Chain().URL(nil).Validate(),
    gv.NewBody("manufacturedAt", nil).Chain().Date(&vgo.IsDateOpts{Format: vgo.ISO8601ZuluLayout, StrictMode: true}).After(&vgo.IsAfterOpts{ComparisonDate: "2020-05-10T00:00:00Z"}).Validate(),
    }
    }

    func getProducts(c *gin.Context) {
    result, err := gv.ValidationResult(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    if len(result) != 0 {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
    "message": "The server encountered validation error",
    "data": nil,
    "errors": result,
    })
    return
    }

    c.AbortWithStatusJSON(http.StatusOK, gin.H{
    "message": "The products found successfully",
    "data": products,
    "errors": nil,
    })
    }

    func getProduct(c *gin.Context) {
    result, err := gv.ValidationResult(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    if len(result) != 0 {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
    "message": "The server encountered validation error",
    "data": nil,
    "errors": result,
    })
    return
    }

    data, err := gv.GetMatchedData(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    id, _ := data.Get(gv.ParamLocation, "id")

    var product *Product

    for _, p := range products {
    if p.ID == id {
    product = &p
    break
    }
    }

    if product == nil {
    c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
    "message": fmt.Sprintf("Product with id %s, not found", id),
    "data": nil,
    "errors": nil,
    })
    return
    } else {
    c.AbortWithStatusJSON(http.StatusOK, gin.H{
    "message": "The product found successfully",
    "data": product,
    "errors": nil,
    })
    return
    }
    }

    func deleteProduct(c *gin.Context) {
    result, err := gv.ValidationResult(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    if len(result) != 0 {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
    "message": "The server encountered validation error",
    "data": nil,
    "errors": result,
    })
    return
    }

    data, err := gv.GetMatchedData(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": "The server encountered an unexpected error.",
    "data": nil,
    "errors": nil,
    })
    return
    }

    id, _ := data.Get(gv.ParamLocation, "id")

    var deletedProduct *Product
    var filteredProducts = []Product{}

    for _, prod := range products {
    if prod.ID == id {
    deletedProduct = &prod
    break
    } else {
    filteredProducts = append(filteredProducts, prod)
    }
    }

    if deletedProduct == nil {
    c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
    "message": fmt.Sprintf("Product with id %s, not found", id),
    "data": nil,
    "errors": nil,
    })
    return
    }

    products = filteredProducts
    c.AbortWithStatusJSON(http.StatusOK, gin.H{
    "message": fmt.Sprintf("Product with id %s, deleted", id),
    "data": deletedProduct,
    "errors": nil,
    })
    }

    func postProduct(c *gin.Context) {
    result, err := gv.ValidationResult(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    if len(result) != 0 {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
    "message": "The server encountered validation error",
    "data": nil,
    "errors": result,
    })
    return
    }

    var newProduct Product

    if err := c.BindJSON(&newProduct); err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    newProduct.ID = fmt.Sprintf("p%d", len(products)+1)
    newProduct.CreatedAt = time.Now()
    newProduct.UpdatedAt = newProduct.CreatedAt

    products = append(products, newProduct)
    c.AbortWithStatusJSON(http.StatusOK, gin.H{
    "message": fmt.Sprintf("Product with id %s, has been created", newProduct.ID),
    "data": newProduct,
    "errors": nil,
    })
    }

    func putProducts(c *gin.Context) {
    result, err := gv.ValidationResult(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    if len(result) != 0 {
    c.AbortWithStatusJSON(http.StatusUnprocessableEntity, gin.H{
    "message": "The server encountered validation error",
    "data": nil,
    "errors": result,
    })
    return
    }

    var newProduct Product

    if err := c.BindJSON(&newProduct); err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": fmt.Sprintf("The server encountered an unexpected error: %v", err),
    "data": nil,
    "errors": nil,
    })
    return
    }

    data, err := gv.GetMatchedData(c)
    if err != nil {
    c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
    "message": "The server encountered an unexpected error.",
    "data": nil,
    "errors": nil,
    })
    return
    }

    id, _ := data.Get(gv.ParamLocation, "id")

    var EditedProduct *Product
    var newProducts = make([]Product, len(products))

    for ind, prod := range products {
    if prod.ID == id {
    newProduct.ID = id
    newProduct.CreatedAt = prod.CreatedAt
    newProduct.UpdatedAt = time.Now()
    newProducts[ind] = newProduct
    EditedProduct = &newProduct
    } else {
    newProducts[ind] = prod
    }
    }

    if EditedProduct == nil {
    c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
    "message": fmt.Sprintf("Product with id %s, not found", id),
    "data": nil,
    "errors": nil,
    })
    return
    }

    products = newProducts
    c.AbortWithStatusJSON(http.StatusOK, gin.H{
    "message": fmt.Sprintf("Product with id %s, has been edited", id),
    "data": EditedProduct,
    "errors": nil,
    })
    }


    Vollständiger Original-Bericht
    Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
    ↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
5 Quellen
Rockwell Automation FactoryTalk Activation Manager
2 Quellen
Jetzt patchen! Angreifer attackieren JFrog Artifactory und machen sich zu Admins
2 Quellen
Zero-Day-Lücke StyleSmuggler in Magento und Adobe Commerce wird aktiv ausgenutzt
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Developing a Simple RESTful API with Gin, ginvalidator, and validatorgo

Thematisch verwandte Begriffe: Developing, Simple, RESTful, with · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...