CRUD
Una API CRUD (create, read, update, delete) completa respaldada por un store en memoria.
Los handlers vinculan el body JSON del request a un struct, acceden al store bajo un lock y
devuelven JSON. Un usuario inexistente devuelve 404, y un id inválido o un nombre vacío devuelve 400.
Servidor
Sección titulada «Servidor»package main
import ( "cmp" "context" "net/http" "slices" "strings" "sync"
"github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware")
type ( user struct { ID int `json:"id"` Name string `json:"name"` })
var ( users = map[int]user{} seq = 1 lock = sync.Mutex{})
//----------// Handlers//----------
// The store holds user values, so handlers copy a user under the lock and write the response after unlocking.
func createUser(c *echo.Context) error { u, err := bindUser(c) if err != nil { return err } lock.Lock() // Assign the ID after binding so an "id" in the request body cannot overwrite another user. u.ID = seq users[u.ID] = u seq++ lock.Unlock() return c.JSON(http.StatusCreated, u)}
func getUser(c *echo.Context) error { id, err := userID(c) if err != nil { return err } lock.Lock() u, ok := users[id] lock.Unlock() if !ok { return echo.NewHTTPError(http.StatusNotFound, "user not found") } return c.JSON(http.StatusOK, u)}
func updateUser(c *echo.Context) error { id, err := userID(c) if err != nil { return err } u, err := bindUser(c) if err != nil { return err } lock.Lock() existing, ok := users[id] if ok { existing.Name = u.Name users[id] = existing } lock.Unlock() if !ok { return echo.NewHTTPError(http.StatusNotFound, "user not found") } return c.JSON(http.StatusOK, existing)}
func deleteUser(c *echo.Context) error { id, err := userID(c) if err != nil { return err } lock.Lock() _, ok := users[id] delete(users, id) lock.Unlock() if !ok { return echo.NewHTTPError(http.StatusNotFound, "user not found") } return c.NoContent(http.StatusNoContent)}
func getAllUsers(c *echo.Context) error { lock.Lock() list := make([]user, 0, len(users)) // non-nil, so an empty store returns [] rather than null for _, u := range users { list = append(list, u) } lock.Unlock() slices.SortFunc(list, func(a, b user) int { return cmp.Compare(a.ID, b.ID) }) return c.JSON(http.StatusOK, list)}
// bindUser binds the JSON request body and requires a non-blank name.func bindUser(c *echo.Context) (user, error) { var u user if err := c.Bind(&u); err != nil { return u, err } if strings.TrimSpace(u.Name) == "" { return u, echo.NewHTTPError(http.StatusBadRequest, "name is required") } return u, nil}
// userID returns the :id path parameter, or a 400 error when it is not a number.func userID(c *echo.Context) (int, error) { id, err := echo.PathParam[int](c, "id") if err != nil { return 0, echo.NewHTTPError(http.StatusBadRequest, "invalid user id") } return id, nil}
func newServer() *echo.Echo { e := echo.New()
// Middleware e.Use(middleware.RequestLogger()) e.Use(middleware.Recover())
// Routes e.GET("/users", getAllUsers) e.POST("/users", createUser) e.GET("/users/:id", getUser) e.PUT("/users/:id", updateUser) e.DELETE("/users/:id", deleteUser) return e}
func main() { e := newServer()
// Start server sc := echo.StartConfig{Address: ":1323"} if err := sc.Start(context.Background(), e); err != nil { e.Logger.Error("failed to start server", "error", err) }}Cliente
Sección titulada «Cliente»Crear usuario
Sección titulada «Crear usuario»Request:
curl -X POST \ -H 'Content-Type: application/json' \ -d '{"name":"Joe Smith"}' \ localhost:1323/usersResponse:
{ "id": 1, "name": "Joe Smith"}Obtener usuario
Sección titulada «Obtener usuario»Request:
curl localhost:1323/users/1Response:
{ "id": 1, "name": "Joe Smith"}Listar usuarios
Sección titulada «Listar usuarios»Request:
curl localhost:1323/usersResponse:
[ { "id": 1, "name": "Joe Smith" }]Actualizar usuario
Sección titulada «Actualizar usuario»Request:
curl -X PUT \ -H 'Content-Type: application/json' \ -d '{"name":"Joe"}' \ localhost:1323/users/1Response:
{ "id": 1, "name": "Joe"}Eliminar usuario
Sección titulada «Eliminar usuario»Request:
curl -X DELETE localhost:1323/users/1Response: 204 No Content.
Usuario inexistente
Sección titulada «Usuario inexistente»Request:
curl localhost:1323/users/9999Response (404 Not Found):
{ "message": "user not found"}