{"id":1642,"date":"2026-09-22T22:42:46","date_gmt":"2026-09-23T01:42:46","guid":{"rendered":"https:\/\/www.linuxpro.com.br\/?p=1642"},"modified":"2026-09-22T22:53:21","modified_gmt":"2026-09-23T01:53:21","slug":"vue-go-echo-v5-parte-3-api-rest-de-tarefas","status":"publish","type":"post","link":"https:\/\/www.linuxpro.com.br\/es\/2026\/09\/vue-go-echo-v5-parte-3-api-rest-de-tarefas\/","title":{"rendered":"Vue.js + Go con Echo v5, parte 3: API REST de tareas"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/09\/vue-go-echo-v5-parte-3.webp\" alt=\"Mascote LinuxPro inserindo um cart\u00e3o de dados JSON num rack de servidores, com o gopher do Go sobre o rack e o cachorro caramelo cyborg trazendo um envelope\" width=\"1486\" height=\"856\" \/><\/p>\n<p><strong>S\u00e9rie Vue.js + Go com Echo v5:<\/strong> <a href=\"\/2026\/09\/vue-go-echo-v5-parte-1-ambiente-e-primeira-api\/\">Parte 1: ambiente e primeira API<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-2-frontend-vue-com-vite\/\">Parte 2: frontend Vue com Vite<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-3-api-rest-de-tarefas\/\">Parte 3: API REST de tarefas<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-4-login-com-sessao-e-cookie\/\">Parte 4: login com sess\u00e3o e cookie<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-5-embed-binario-unico-e-docker\/\">Parte 5: embed, bin\u00e1rio \u00fanico e Docker<\/a><\/p>\n<p><strong>C\u00f3digo completo:<\/strong> todos os arquivos da s\u00e9rie est\u00e3o no <a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08\">gist vue-go-echo-v5<\/a>, com um README que mostra em qual pasta cada um fica.<\/p>\n<p>Com o Vue conversando com o Go pelo proxy do Vite (<a href=\"\/2026\/09\/vue-go-echo-v5-parte-2-frontend-vue-com-vite\/\">parte 2<\/a>), chegou a hora do backend de verdade. Nesta parte escrevemos uma <strong>API REST de tarefas no Echo v5<\/strong>: listar, criar, marcar como feita e remover. No caminho aparecem os recursos que voc\u00ea vai usar em qualquer API com Echo: grupo de rotas, par\u00e2metros de caminho, <code>Bind<\/code> de JSON, valida\u00e7\u00e3o, <code>HTTPError<\/code> e c\u00f3digos de status corretos. No fim, a tela de tarefas do Vue passa a funcionar.<\/p>\n<p><!-- more --><\/p>\n<h2>O contrato da API<\/h2>\n<p>Antes do c\u00f3digo, as rotas. Todas ficam sob <code>\/api<\/code>:<\/p>\n<ul>\n<li><code>GET \/api\/tasks<\/code>: lista as tarefas, <strong>200<\/strong>;<\/li>\n<li><code>POST \/api\/tasks<\/code> com <code>{\"title\": \"...\"}<\/code>: cria, <strong>201<\/strong> com a tarefa criada;<\/li>\n<li><code>PATCH \/api\/tasks\/:id<\/code> com <code>{\"done\": true}<\/code> ou <code>{\"title\": \"...\"}<\/code>: altera, <strong>200<\/strong>;<\/li>\n<li><code>DELETE \/api\/tasks\/:id<\/code>: remove, <strong>204<\/strong> sem corpo.<\/li>\n<\/ul>\n<p>Erros seguem o padr\u00e3o do Echo, <code>{\"message\": \"...\"}<\/code>: <strong>400<\/strong> para JSON ou id inv\u00e1lido, <strong>422<\/strong> para t\u00edtulo vazio ou longo demais, <strong>404<\/strong> para tarefa inexistente. \u00c9 esse <code>message<\/code> que o <code>api.js<\/code> da parte 2 mostra na tela.<\/p>\n<h2>O modelo e o armazenamento em mem\u00f3ria<\/h2>\n<p>Para manter o foco no Echo, as tarefas ficam em mem\u00f3ria, protegidas por um <code>sync.Mutex<\/code>: o servidor atende v\u00e1rias requisi\u00e7\u00f5es ao mesmo tempo, e sem a trava dois <code>POST<\/code> simult\u00e2neos corromperiam a lista. Reiniciar o processo apaga tudo. Trocar por SQLite ou PostgreSQL depois muda s\u00f3 este arquivo.<\/p>\n<p>Crie <a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-tasks-go\"><code>internal\/api\/tasks.go<\/code><\/a>:<\/p>\n<pre><code class=\"language-go\">package api\n\nimport (\n\t\"net\/http\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com\/labstack\/echo\/v5\"\n)\n\n\/\/ Task \u00e9 uma tarefa da lista.\ntype Task struct {\n\tID        int       `json:\"id\"`\n\tTitle     string    `json:\"title\"`\n\tDone      bool      `json:\"done\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n}\n\n\/\/ Tasks guarda as tarefas em mem\u00f3ria. Reiniciar o processo apaga tudo.\ntype Tasks struct {\n\tmu     sync.Mutex\n\tnextID int\n\titems  []Task\n}\n\nfunc NewTasks() *Tasks { return &amp;Tasks{nextID: 1} }<\/code><\/pre>\n<p>As tags <code>json:\"...\"<\/code> definem os nomes dos campos no JSON: <code>created_at<\/code> em vez de <code>CreatedAt<\/code>.<\/p>\n<h2>Os handlers<\/h2>\n<p>No Echo v5, um handler \u00e9 uma fun\u00e7\u00e3o <code>func(c *echo.Context) error<\/code>. Aqui eles s\u00e3o m\u00e9todos de <code>*Tasks<\/code>, o que d\u00e1 acesso \u00e0 lista sem vari\u00e1vel global. Continue em <code>tasks.go<\/code>:<\/p>\n<pre><code class=\"language-go\">func (t *Tasks) List(c *echo.Context) error {\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\treturn c.JSON(http.StatusOK, append([]Task{}, t.items...))\n}\n\ntype taskInput struct {\n\tTitle string `json:\"title\"`\n\tDone  *bool  `json:\"done\"`\n}\n\nfunc (t *Tasks) Create(c *echo.Context) error {\n\tvar in taskInput\n\tif err := c.Bind(&amp;in); err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"JSON inv\u00e1lido\")\n\t}\n\ttitle := strings.TrimSpace(in.Title)\n\tif title == \"\" || len(title) &gt; 200 {\n\t\treturn echo.NewHTTPError(http.StatusUnprocessableEntity, \"o t\u00edtulo precisa ter de 1 a 200 caracteres\")\n\t}\n\tt.mu.Lock()\n\ttask := Task{ID: t.nextID, Title: title, CreatedAt: time.Now().UTC()}\n\tt.nextID++\n\tt.items = append(t.items, task)\n\tt.mu.Unlock()\n\treturn c.JSON(http.StatusCreated, task)\n}<\/code><\/pre>\n<p>Alguns detalhes:<\/p>\n<ul>\n<li><code>append([]Task{}, t.items...)<\/code> devolve uma c\u00f3pia. Com a lista vazia, o JSON sai <code>[]<\/code>, e n\u00e3o <code>null<\/code>, o que evita um <code>v-for<\/code> quebrado no Vue;<\/li>\n<li><code>c.Bind(&amp;in)<\/code> l\u00ea o corpo conforme o <code>Content-Type<\/code>. Para JSON, precisa do cabe\u00e7alho <code>Content-Type: application\/json<\/code>, que o <code>api.js<\/code> j\u00e1 envia;<\/li>\n<li><code>echo.NewHTTPError(c\u00f3digo, mensagem)<\/code> interrompe o handler e vira a resposta <code>{\"message\": ...}<\/code> com o status certo;<\/li>\n<li><code>Done<\/code> \u00e9 <code>*bool<\/code> na entrada para distinguir \u201cn\u00e3o enviado\u201d de <code>false<\/code>: sem o ponteiro, mudar s\u00f3 o t\u00edtulo desmarcaria a tarefa.<\/li>\n<\/ul>\n<p>Atualizar e remover leem o <code>:id<\/code> do caminho com <code>c.Param<\/code>:<\/p>\n<pre><code class=\"language-go\">func (t *Tasks) Update(c *echo.Context) error {\n\tid, err := strconv.Atoi(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"id inv\u00e1lido\")\n\t}\n\tvar in taskInput\n\tif err := c.Bind(&amp;in); err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"JSON inv\u00e1lido\")\n\t}\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\tfor i := range t.items {\n\t\tif t.items[i].ID == id {\n\t\t\tif title := strings.TrimSpace(in.Title); title != \"\" {\n\t\t\t\tt.items[i].Title = title\n\t\t\t}\n\t\t\tif in.Done != nil {\n\t\t\t\tt.items[i].Done = *in.Done\n\t\t\t}\n\t\t\treturn c.JSON(http.StatusOK, t.items[i])\n\t\t}\n\t}\n\treturn echo.NewHTTPError(http.StatusNotFound, \"tarefa n\u00e3o encontrada\")\n}\n\nfunc (t *Tasks) Delete(c *echo.Context) error {\n\tid, err := strconv.Atoi(c.Param(\"id\"))\n\tif err != nil {\n\t\treturn echo.NewHTTPError(http.StatusBadRequest, \"id inv\u00e1lido\")\n\t}\n\tt.mu.Lock()\n\tdefer t.mu.Unlock()\n\tfor i := range t.items {\n\t\tif t.items[i].ID == id {\n\t\t\tt.items = append(t.items[:i], t.items[i+1:]...)\n\t\t\treturn c.NoContent(http.StatusNoContent)\n\t\t}\n\t}\n\treturn echo.NewHTTPError(http.StatusNotFound, \"tarefa n\u00e3o encontrada\")\n}<\/code><\/pre>\n<h2>Registre as rotas com um grupo<\/h2>\n<p>Um <strong>grupo<\/strong> aplica um prefixo, e opcionalmente middlewares, a v\u00e1rias rotas. Crie <a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-routes-go\"><code>internal\/api\/routes.go<\/code><\/a>. Nesta parte as rotas ainda s\u00e3o p\u00fablicas; na parte 4 entra o login:<\/p>\n<pre><code class=\"language-go\">\/\/ Package api re\u00fane as rotas JSON da aplica\u00e7\u00e3o.\npackage api\n\nimport (\n\t\"net\/http\"\n\n\t\"github.com\/labstack\/echo\/v5\"\n)\n\n\/\/ Register liga as rotas em \/api.\nfunc Register(e *echo.Echo, tasks *Tasks) {\n\tg := e.Group(\"\/api\")\n\n\tg.GET(\"\/health\", func(c *echo.Context) error {\n\t\treturn c.JSON(http.StatusOK, map[string]string{\"status\": \"ok\"})\n\t})\n\tg.GET(\"\/tasks\", tasks.List)\n\tg.POST(\"\/tasks\", tasks.Create)\n\tg.PATCH(\"\/tasks\/:id\", tasks.Update)\n\tg.DELETE(\"\/tasks\/:id\", tasks.Delete)\n}<\/code><\/pre>\n<p>E o <code>main.go<\/code> passa a delegar as rotas ao pacote <code>api<\/code>. A rota <code>\/api\/health<\/code> da parte 1 mudou para dentro do grupo:<\/p>\n<pre><code class=\"language-go\">package main\n\nimport (\n\t\"log\/slog\"\n\n\t\"github.com\/exemplo\/vueapp\/internal\/api\"\n\t\"github.com\/labstack\/echo\/v5\"\n\t\"github.com\/labstack\/echo\/v5\/middleware\"\n)\n\nfunc main() {\n\te := echo.New()\n\te.Use(middleware.RequestLogger())\n\te.Use(middleware.Recover())\n\n\tapi.Register(e, api.NewTasks())\n\n\tif err := e.Start(\"127.0.0.1:8080\"); err != nil {\n\t\tslog.Error(\"servidor\", \"error\", err)\n\t}\n}<\/code><\/pre>\n<h2>Teste com curl<\/h2>\n<pre><code class=\"language-bash\">go run .<\/code><\/pre>\n<pre><code class=\"language-bash\">B=http:\/\/127.0.0.1:8080\ncurl -s -H 'Content-Type: application\/json' -d '{\"title\":\"Estudar Echo v5\"}' $B\/api\/tasks\ncurl -s -H 'Content-Type: application\/json' -d '{\"title\":\"  \"}' $B\/api\/tasks\ncurl -s -X PATCH -H 'Content-Type: application\/json' -d '{\"done\":true}' $B\/api\/tasks\/1\ncurl -s $B\/api\/tasks\ncurl -s -o \/dev\/null -w '%{http_code}\\n' -X DELETE $B\/api\/tasks\/1\ncurl -s $B\/api\/naoexiste<\/code><\/pre>\n<p>Sa\u00edda do nosso teste:<\/p>\n<pre><code class=\"language-text\">{\"id\":1,\"title\":\"Estudar Echo v5\",\"done\":false,\"created_at\":\"2026-09-23T01:31:05.742644306Z\"}\n{\"message\":\"o t\u00edtulo precisa ter de 1 a 200 caracteres\"}\n{\"id\":1,\"title\":\"Estudar Echo v5\",\"done\":true,\"created_at\":\"2026-09-23T01:31:05.742644306Z\"}\n[{\"id\":1,\"title\":\"Estudar Echo v5\",\"done\":true,\"created_at\":\"2026-09-23T01:31:05.742644306Z\"}]\n204\n{\"message\":\"Not Found\"}<\/code><\/pre>\n<p>Cada resposta tem o status que o contrato promete, inclusive o 404 padr\u00e3o do Echo para uma rota que n\u00e3o existe.<\/p>\n<h2>A tela de tarefas no Vue<\/h2>\n<p>Substitua a view provis\u00f3ria da parte 2 por <a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-tasksview-vue\"><code>web\/src\/views\/TasksView.vue<\/code><\/a> completo. O usu\u00e1rio logado e o bot\u00e3o de sair entram agora e come\u00e7am a funcionar na parte 4:<\/p>\n<pre><code class=\"language-html\">&lt;script setup&gt;\nimport { onMounted, ref } from 'vue'\nimport { useRouter } from 'vue-router'\nimport { api } from '..\/api'\n\nconst router = useRouter()\nconst user = ref('')\nconst tasks = ref([])\nconst title = ref('')\nconst error = ref('')\n\nasync function load() {\n  user.value = (await api('GET', '\/me')).username\n  tasks.value = await api('GET', '\/tasks')\n}\n\nasync function add() {\n  error.value = ''\n  try {\n    tasks.value.push(await api('POST', '\/tasks', { title: title.value }))\n    title.value = ''\n  } catch (e) {\n    error.value = e.message\n  }\n}\n\nasync function toggle(task) {\n  Object.assign(task, await api('PATCH', `\/tasks\/${task.id}`, { done: !task.done }))\n}\n\nasync function remove(task) {\n  await api('DELETE', `\/tasks\/${task.id}`)\n  tasks.value = tasks.value.filter((t) =&gt; t.id !== task.id)\n}\n\nasync function logout() {\n  await api('POST', '\/logout')\n  router.push('\/login')\n}\n\nonMounted(load)\n&lt;\/script&gt;\n\n&lt;template&gt;\n  &lt;section class=\"card\"&gt;\n    &lt;header&gt;\n      &lt;h1&gt;Tarefas&lt;\/h1&gt;\n      &lt;span&gt;{{ user }} \u00b7 &lt;a href=\"#\" @click.prevent=\"logout\"&gt;Sair&lt;\/a&gt;&lt;\/span&gt;\n    &lt;\/header&gt;\n    &lt;form class=\"row\" @submit.prevent=\"add\"&gt;\n      &lt;input v-model=\"title\" placeholder=\"Nova tarefa\" maxlength=\"200\" \/&gt;\n      &lt;button&gt;Adicionar&lt;\/button&gt;\n    &lt;\/form&gt;\n    &lt;p v-if=\"error\" class=\"error\"&gt;{{ error }}&lt;\/p&gt;\n    &lt;ul&gt;\n      &lt;li v-for=\"task in tasks\" :key=\"task.id\" :class=\"{ done: task.done }\"&gt;\n        &lt;label&gt;&lt;input type=\"checkbox\" :checked=\"task.done\" @change=\"toggle(task)\" \/&gt; {{ task.title }}&lt;\/label&gt;\n        &lt;button class=\"link\" @click=\"remove(task)\"&gt;remover&lt;\/button&gt;\n      &lt;\/li&gt;\n    &lt;\/ul&gt;\n    &lt;p v-if=\"!tasks.length\" class=\"muted\"&gt;Nenhuma tarefa ainda.&lt;\/p&gt;\n  &lt;\/section&gt;\n&lt;\/template&gt;<\/code><\/pre>\n<p>O padr\u00e3o \u00e9 sempre o mesmo: chamar a API e atualizar o estado reativo com a resposta do servidor. O <code>toggle<\/code> copia para a tarefa o que o Go devolveu, ent\u00e3o a tela nunca mostra um estado que o backend n\u00e3o aceitou. Enquanto a rota <code>\/api\/me<\/code> n\u00e3o existe, remova a primeira linha do <code>load()<\/code> para testar.<\/p>\n<p>Um CSS simples deixa a tela apresent\u00e1vel. Coloque em <a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-style-css\"><code>web\/src\/style.css<\/code><\/a>:<\/p>\n<pre><code class=\"language-css\">:root { font-family: system-ui, sans-serif; color: #1f2937; background: #f3f4f6; }\nbody { margin: 0; }\n.container { max-width: 480px; margin: 4rem auto; padding: 0 1rem; }\n.card { background: #fff; border-radius: 8px; padding: 1.5rem; box-shadow: 0 1px 3px rgb(0 0 0 \/ 0.1); }\n.card label { display: block; margin-bottom: .75rem; }\ninput { width: 100%; box-sizing: border-box; padding: .5rem; margin-top: .25rem; }\ninput[type=checkbox] { width: auto; margin: 0 .5rem 0 0; }\nbutton { padding: .5rem 1rem; background: #2563eb; color: #fff; border: 0; border-radius: 4px; cursor: pointer; }\nbutton.link { background: none; color: #b91c1c; padding: 0; }\nheader { display: flex; justify-content: space-between; align-items: baseline; }\n.row { display: flex; gap: .5rem; margin-bottom: 1rem; }\n.row input { margin: 0; }\nul { list-style: none; padding: 0; }\nli { display: flex; justify-content: space-between; padding: .4rem 0; border-bottom: 1px solid #e5e7eb; }\nli label { margin: 0; }\nli.done label { text-decoration: line-through; color: #9ca3af; }\n.error { color: #b91c1c; }\n.muted { color: #6b7280; }<\/code><\/pre>\n<p><img decoding=\"async\" src=\"\/wp-content\/uploads\/2026\/09\/vue-go-echo-tarefas.webp\" alt=\"Tela de tarefas em Vue consumindo a API Go com Echo v5, com tr\u00eas tarefas e uma marcada como conclu\u00edda\" width=\"900\" height=\"560\" loading=\"lazy\" \/><\/p>\n<h2>Arquivos desta parte no gist<\/h2>\n<p>Cada arquivo abre direto no <a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08\">gist da s\u00e9rie<\/a>. O gist traz a <strong>vers\u00e3o final<\/strong> do projeto: <code>main.go<\/code> e <code>routes.go<\/code> ainda ganham login na parte 4 e o frontend embutido na parte 5.<\/p>\n<ul>\n<li><a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-tasks-go\"><code>tasks.go<\/code><\/a> (em <code>internal\/api\/tasks.go<\/code>)<\/li>\n<li><a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-routes-go\"><code>routes.go<\/code><\/a> (em <code>internal\/api\/routes.go<\/code>)<\/li>\n<li><a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-tasksview-vue\"><code>TasksView.vue<\/code><\/a> (em <code>web\/src\/views\/TasksView.vue<\/code>)<\/li>\n<li><a href=\"https:\/\/gist.github.com\/jniltinho\/c95a0e7bbad4afe779cf143674cbef08#file-style-css\"><code>style.css<\/code><\/a> (em <code>web\/src\/style.css<\/code>)<\/li>\n<\/ul>\n<h2>Pr\u00f3ximo passo<\/h2>\n<p>A API funciona, mas qualquer um consegue apagar as suas tarefas. Na <a href=\"\/2026\/09\/vue-go-echo-v5-parte-4-login-com-sessao-e-cookie\/\">parte 4<\/a> criamos o login com senha em bcrypt, a sess\u00e3o em cookie <code>HttpOnly<\/code>, um middleware do Echo que protege as rotas e a tela de login no Vue.<\/p>\n<p><strong>S\u00e9rie Vue.js + Go com Echo v5:<\/strong> <a href=\"\/2026\/09\/vue-go-echo-v5-parte-1-ambiente-e-primeira-api\/\">Parte 1: ambiente e primeira API<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-2-frontend-vue-com-vite\/\">Parte 2: frontend Vue com Vite<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-3-api-rest-de-tarefas\/\">Parte 3: API REST de tarefas<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-4-login-com-sessao-e-cookie\/\">Parte 4: login com sess\u00e3o e cookie<\/a> \u00b7 <a href=\"\/2026\/09\/vue-go-echo-v5-parte-5-embed-binario-unico-e-docker\/\">Parte 5: embed, bin\u00e1rio \u00fanico e Docker<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Escribe una API REST de tareas con Echo v5: grupos de rutas, Bind de JSON, validaci\u00f3n, HTTPError y la pantalla de tareas en Vue.<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[45,2],"tags":[424,58,31,468],"class_list":["post-1642","post","type-post","status-publish","format-standard","hentry","category-desenv","category-linux","tag-api","tag-go","tag-golang","tag-vue"],"_links":{"self":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/1642","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/comments?post=1642"}],"version-history":[{"count":2,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/1642\/revisions"}],"predecessor-version":[{"id":1657,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/1642\/revisions\/1657"}],"wp:attachment":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/media?parent=1642"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/categories?post=1642"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/tags?post=1642"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}