{"id":3901,"date":"2017-08-29T07:00:39","date_gmt":"2017-08-29T14:00:39","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=3901"},"modified":"2025-06-13T18:46:00","modified_gmt":"2025-06-14T01:46:00","slug":"developing-user-profile-store-golang-nosql-database","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/developing-user-profile-store-golang-nosql-database\/","title":{"rendered":"Desenvolvimento de um armazenamento de perfil de usu\u00e1rio com Golang e um banco de dados NoSQL"},"content":{"rendered":"<p>Lembre-se da s\u00e9rie de tutoriais que escrevi com rela\u00e7\u00e3o a <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/creating-user-profile-store-with-node-js-nosql-database\/\" target=\"_blank\" rel=\"noopener noreferrer\">Cria\u00e7\u00e3o de um armazenamento de perfil de usu\u00e1rio com Node.js e NoSQL<\/a>? Esse tutorial cobriu uma s\u00e9rie de assuntos, desde a cria\u00e7\u00e3o de uma API RESTful com Node.js, o tratamento de sess\u00f5es de usu\u00e1rios, a modelagem de dados e, \u00e9 claro, o armazenamento de dados associados aos usu\u00e1rios.<\/p>\n<p>E se quis\u00e9ssemos usar os mesmos conceitos e aplic\u00e1-los com Golang em vez de JavaScript com Node.js?<\/p>\n<p>Veremos como desenvolver um armazenamento de perfil de usu\u00e1rio com Golang e <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/\" target=\"_blank\" rel=\"noopener noreferrer\">Servidor Couchbase<\/a> que atua como um substituto modular para a alternativa do Node.js.<\/p>\n<p><!--more--><\/p>\n<p>Daqui para frente, vamos supor que voc\u00ea tenha o Go instalado e configurado, bem como o Couchbase Server. N\u00e3o \u00e9 importante se voc\u00ea j\u00e1 viu a instru\u00e7\u00e3o <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/creating-user-profile-store-with-node-js-nosql-database\/\" target=\"_blank\" rel=\"noopener noreferrer\">Vers\u00e3o Node.js deste tutorial<\/a> porque vamos revisar tudo.<\/p>\n<p>Caso voc\u00ea n\u00e3o saiba o que \u00e9 um armazenamento de perfil de usu\u00e1rio ou o que ele faz, trata-se simplesmente de uma solu\u00e7\u00e3o para armazenar informa\u00e7\u00f5es sobre usu\u00e1rios e informa\u00e7\u00f5es associadas a eles. Veja, por exemplo, um blog. Um blog pode ter v\u00e1rios autores, que s\u00e3o tecnicamente usu\u00e1rios. Cada autor escrever\u00e1 conte\u00fado e esse conte\u00fado ser\u00e1 associado ao usu\u00e1rio espec\u00edfico que o escreveu. Cada autor tamb\u00e9m ter\u00e1 seu pr\u00f3prio m\u00e9todo de login no blog.<\/p>\n<p>Como os modelos de dados do usu\u00e1rio podem mudar com muita frequ\u00eancia, o uso de um banco de dados NoSQL com um modelo de armazenamento flex\u00edvel costuma ser mais eficaz do que uma alternativa RDBMS. Mais informa\u00e7\u00f5es sobre o aspecto da modelagem de dados podem ser encontradas neste artigo,\u00a0<a href=\"https:\/\/www.couchbase.com\/blog\/pt\/user-profile-store-advanced-data-modeling\/\" target=\"_blank\" rel=\"noopener noreferrer\">Armazenamento de perfil de usu\u00e1rio: Modelagem avan\u00e7ada de dados<\/a>, escrito por Kirk Kirkconnell.<\/p>\n<h2>Gerenciamento de usu\u00e1rios Golang para criar um novo projeto<\/h2>\n<p>Vamos passar todo o tempo em um \u00fanico arquivo Go. Em algum lugar em seu <strong>$GOPATH<\/strong>, crie um arquivo chamado <strong>main.go<\/strong>.<\/p>\n<p>Tamb\u00e9m precisaremos de algumas depend\u00eancias, tanto para o Couchbase quanto para outros pacotes. Na linha de comando, execute o seguinte:<\/p>\n<pre class=\"lang:default decode:true\">go get github.com\/couchbase\/gocb\r\ngo get github.com\/gorilla\/context\r\ngo get github.com\/gorilla\/handlers\r\ngo get github.com\/gorilla\/mux\r\ngo get github.com\/satori\/go.uuid<\/pre>\n<p>As depend\u00eancias acima permitir\u00e3o que nos comuniquemos com o Couchbase Server, geremos valores de UUID e criemos uma API RESTful com tratamento de compartilhamento de recursos entre origens (CORS).<\/p>\n<p>A pr\u00f3xima etapa \u00e9 criar um c\u00f3digo padr\u00e3o para o nosso projeto. Abra a se\u00e7\u00e3o\u00a0<strong>main.go<\/strong> e inclua o seguinte:<\/p>\n<pre class=\"lang:default decode:true\">pacote principal\r\n\r\nimport (\r\n\t\"encoding\/json\"\r\n\t\"fmt\"\r\n\t\"log\"\r\n\t\"net\/http\"\r\n\t\"strings\"\r\n\t\"time\" (tempo)\r\n\r\n\t\"golang.org\/x\/crypto\/bcrypt\"\r\n\r\n\t\"github.com\/couchbase\/gocb\"\r\n\t\"github.com\/gorilla\/context\"\r\n\t\"github.com\/gorilla\/handlers\"\r\n\t\"github.com\/gorilla\/mux\"\r\n\tuuid \"github.com\/satori\/go.uuid\"\r\n)\r\n\r\ntype Account struct {\r\n\tType string `json: \"type,omitempty\"`\r\n\tPid string `json: \"pid,omitempty\"`\r\n\tEmail string `json: \"email,omitempty\"`\r\n\tPassword string `json: \"password,omitempty\"`\r\n}\r\n\r\ntype Profile struct {\r\n\tTipo string `json: \"tipo,omitempty\"`\r\n\tFirstname string `json: \"firstname,omitempty\"`\r\n\tLastname string `json: \"lastname,omitempty\"`\r\n}\r\n\r\ntype Session struct {\r\n\tType string `json: \"type,omitempty\"`\r\n\tPid string `json: \"pid,omitempty\"`\r\n}\r\n\r\ntype Blog struct {\r\n\tType string `json: \"type,omitempty\"`\r\n\tPid string `json: \"pid,omitempty\"`\r\n\tTitle string `json: \"title,omitempty\"`\r\n\tContent string `json: \"content,omitempty\"`\r\n\tTimestamp int `json: \"timestamp,omitempty\"`\r\n}\r\n\r\nvar bucket *gocb.Bucket\r\n\r\nfunc Validate(next http.HandlerFunc) http.HandlerFunc {}\r\n\r\nfunc RegisterEndpoint(w http.ResponseWriter, req *http.Request) {}\r\nfunc LoginEndpoint(w http.ResponseWriter, req *http.Request) {}\r\nfunc AccountEndpoint(w http.ResponseWriter, req *http.Request) {}\r\nfunc BlogsEndpoint(w http.ResponseWriter, req *http.Request) {}\r\nfunc BlogEndpoint(w http.ResponseWriter, req *http.Request) {}\r\n\r\nfunc main() {\r\n\tfmt.Println(\"Iniciando o servidor Go...\")\r\n\troteador := mux.NewRouter()\r\n\tcluster, _ := gocb.Connect(\"couchbase:\/\/localhost\")\r\n\tbucket, _ = cluster.OpenBucket(\"default\", \"\")\r\n\troteador.HandleFunc(\"\/account\", RegisterEndpoint).Methods(\"POST\")\r\n\troteador.HandleFunc(\"\/login\", LoginEndpoint).Methods(\"POST\")\r\n\trouter.HandleFunc(\"\/account\", Validate(AccountEndpoint)).Methods(\"GET\")\r\n\trouter.HandleFunc(\"\/blogs\", Validate(BlogsEndpoint)).Methods(\"GET\")\r\n\troteador.HandleFunc(\"\/blog\", Validate(BlogEndpoint)).Methods(\"POST\")\r\n\tlog.Fatal(http.ListenAndServe(\":3000\", handlers.CORS(handlers.AllowedHeaders([]string{\"X-Requested-With\", \"Content-Type\", \"Authorization\"}), handlers.AllowedMethods([]string{\"GET\", \"POST\", \"PUT\", \"HEAD\", \"OPTIONS\"}), handlers.AllowedOrigins([]string{\"*\"}))(router)))\r\n}<\/pre>\n<p>No exemplo acima, voc\u00ea notar\u00e1 que criamos algumas estruturas de dados para representar nossos dados. Estamos adotando a ideia de uma plataforma de blog.<\/p>\n<p>O aplicativo ter\u00e1 cinco pontos de extremidade da API RESTful, um m\u00e9todo validador para nossas sess\u00f5es de usu\u00e1rio e uma vari\u00e1vel global do Couchbase que nos permitir\u00e1 acessar nossa inst\u00e2ncia aberta em qualquer lugar do aplicativo.<\/p>\n<pre class=\"lang:default decode:true\">roteador.HandleFunc(\"\/account\", Validate(AccountEndpoint)).Methods(\"GET\")\r\nrouter.HandleFunc(\"\/blogs\", Validate(BlogsEndpoint)).Methods(\"GET\")\r\nroteador.HandleFunc(\"\/blog\", Validate(BlogEndpoint)).Methods(\"POST\")<\/pre>\n<p>Observe que os tr\u00eas pontos de extremidade acima t\u00eam o <code>Validar<\/code> anexada a eles. Isso significa que o usu\u00e1rio deve ter se autenticado e estar fornecendo uma sess\u00e3o v\u00e1lida para progredir. Nesse sentido, o <code>Validar<\/code> atua como um middleware.<\/p>\n<p>Como planejamos consultar os dados, mais especificamente, os artigos do blog para um determinado usu\u00e1rio, precisamos criar um \u00edndice. Usando o painel da Web, a CLI do Couchbase ou o aplicativo Go, execute o seguinte:<\/p>\n<pre class=\"lang:default decode:true\">CREATE INDEX `blogbyuser` ON `default`(type, pid);<\/pre>\n<p>O \u00edndice acima nos permitir\u00e1 consultar por um <code>tipo<\/code> bem como uma propriedade <code>pid<\/code> propriedade.<\/p>\n<p>Agora podemos come\u00e7ar a preencher as lacunas de cada um de nossos endpoints de API.<\/p>\n<h2>Permitir que os usu\u00e1rios registrem novas informa\u00e7\u00f5es no Profile Store<\/h2>\n<p>Como n\u00e3o temos usu\u00e1rios no armazenamento de perfis at\u00e9 o momento, faria sentido criar um ponto de extremidade que ofere\u00e7a suporte \u00e0 cria\u00e7\u00e3o de novos usu\u00e1rios.<\/p>\n<p>\u00c9 uma boa pr\u00e1tica nunca armazenar informa\u00e7\u00f5es de credenciais do tipo nome de usu\u00e1rio e senha com informa\u00e7\u00f5es reais do usu\u00e1rio. Por esse motivo, criar um novo usu\u00e1rio significa criar um <strong>perfil<\/strong> bem como um documento <strong>conta<\/strong> documento. O <strong>conta<\/strong> far\u00e1 refer\u00eancia ao documento <strong>perfil<\/strong> document. Ambos ser\u00e3o modelados de acordo com nossas estruturas de dados Go que vimos no c\u00f3digo padr\u00e3o.<\/p>\n<p>No\u00a0<strong>main.go<\/strong> adicione o seguinte:<\/p>\n<pre class=\"lang:default decode:true\">func RegisterEndpoint(w http.ResponseWriter, req *http.Request) {\r\n\tvar data map[string]interface{}\r\n\t_ = json.NewDecoder(req.Body).Decode(&amp;data)\r\n\tid := uuid.NewV4().String()\r\n\tpasswordHash, _ := bcrypt.GenerateFromPassword([]byte(data[\"password\"].(string)), 10)\r\n\taccount := Account{\r\n\t\tType:     \"account\",\r\n\t\tPid: id,\r\n\t\tE-mail: data[\"email\"].(string),\r\n\t\tSenha: string(passwordHash),\r\n\t}\r\n\tprofile := Profile{\r\n\t\tType:      \"profile\",\r\n\t\tFirstname: data[\"firstname\"].(string),\r\n\t\tLastname: data[\"lastname\"].(string),\r\n\t}\r\n\t_, err := bucket.Insert(id, profile, 0)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\t_, err = bucket.Insert(data[\"email\"].(string), account, 0)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\tjson.NewEncoder(w).Encode(account)\r\n}<\/pre>\n<p>H\u00e1 algumas coisas importantes acontecendo no c\u00f3digo do endpoint acima.<\/p>\n<p>Primeiro, estamos aceitando os dados JSON que foram enviados com o corpo POST da solicita\u00e7\u00e3o do cliente. Estamos gerando um ID exclusivo para o <strong>perfil<\/strong> documento e fazer o hash da senha para mant\u00ea-la em seguran\u00e7a com o BCrypt.<\/p>\n<p>Quando se trata de realmente salvar os dados, o perfil do usu\u00e1rio receber\u00e1 uma identifica\u00e7\u00e3o exclusiva, enquanto a conta receber\u00e1 um endere\u00e7o de e-mail como identifica\u00e7\u00e3o e uma refer\u00eancia \u00e0 identifica\u00e7\u00e3o do perfil no documento.<\/p>\n<p>Ao seguir essa abordagem, a <strong>conta<\/strong> pode ser facilmente ampliado para outras formas de credenciais. Por exemplo, o documento da conta poderia ser renomeado como <strong>basicauth<\/strong>e poder\u00edamos ter o Facebook, o Twitter, etc., que fazem refer\u00eancia \u00e0s informa\u00e7\u00f5es do perfil.<\/p>\n<h2>Implementa\u00e7\u00e3o de um token de sess\u00e3o para usu\u00e1rios<\/h2>\n<p>Fazer login no aplicativo por meio de nossos dois documentos \u00e9 um pouco diferente. Nunca \u00e9 uma boa ideia divulgar o nome de usu\u00e1rio e a senha mais do que o absolutamente necess\u00e1rio.<\/p>\n<p>Por esse motivo, \u00e9 uma boa ideia usar um token de sess\u00e3o que represente o usu\u00e1rio. Esse token pode expirar e n\u00e3o cont\u00e9m informa\u00e7\u00f5es confidenciais.<\/p>\n<p>Use o seguinte c\u00f3digo de login para o\u00a0<strong>main.go<\/strong> file:<\/p>\n<pre class=\"lang:default decode:true\">func LoginEndpoint(w http.ResponseWriter, req *http.Request) {\r\n\tvar data Account\r\n\tvar account Conta\r\n\t_ = json.NewDecoder(req.Body).Decode(&amp;data)\r\n\t_, err := bucket.Get(data.Email, &amp;account)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\terr = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(data.Password))\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\tsess\u00e3o := Session{\r\n\t\tType: \"session\",\r\n\t\tPid: account.Pid,\r\n\t}\r\n\tvar result map[string]interface{}\r\n\tresult = make(map[string]interface{})\r\n\tresult[\"sid\"] = uuid.NewV4().String()\r\n\t_, err = bucket.Insert(result[\"sid\"].(string), &amp;session, 3600)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\tjson.NewEncoder(w).Encode(result)\r\n}<\/pre>\n<p>Quando o e-mail e a senha s\u00e3o passados para esse ponto de extremidade, o <strong>conta<\/strong> \u00e9 recuperado com base no e-mail que foi fornecido. A senha com hash dentro desse documento \u00e9 ent\u00e3o comparada com a senha sem hash.<\/p>\n<p>Se as credenciais forem v\u00e1lidas, um <strong>sess\u00e3o<\/strong> \u00e9 criado. Esse documento de sess\u00e3o tem uma chave exclusiva, mas faz refer\u00eancia \u00e0 chave do <strong>perfil<\/strong> documento. Um tempo de expira\u00e7\u00e3o tamb\u00e9m \u00e9 adicionado ao documento. Quando o tempo de expira\u00e7\u00e3o passar, o documento ser\u00e1 automaticamente removido do Couchbase sem qualquer interven\u00e7\u00e3o do aplicativo ou do usu\u00e1rio. Isso ajuda a proteger a conta.<\/p>\n<p>Com as contas funcionais, precisamos nos preocupar em associar informa\u00e7\u00f5es aos usu\u00e1rios.<\/p>\n<h2>Gerenciamento de informa\u00e7\u00f5es do usu\u00e1rio no Profile Store por meio de um token de sess\u00e3o<\/h2>\n<p>Quando um usu\u00e1rio tenta fazer algo espec\u00edfico para si mesmo, precisamos validar se ele \u00e9 quem deveria ser e se as informa\u00e7\u00f5es que est\u00e1 alterando s\u00e3o aplicadas \u00e0 pessoa correta.<\/p>\n<p>\u00c9 nesse ponto que o middleware de valida\u00e7\u00e3o de token de sess\u00e3o entra em a\u00e7\u00e3o.<\/p>\n<pre class=\"lang:default decode:true\">func Validate(next http.HandlerFunc) http.HandlerFunc {\r\n\treturn http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {\r\n\t\tauthorizationHeader := req.Header.Get(\"authorization\")\r\n\t\tse authorizationHeader != \"\" {\r\n\t\t\tbearerToken := strings.Split(authorizationHeader, \" \")\r\n\t\t\tse len(bearerToken) == 2 {\r\n\t\t\t\tvar session Session\r\n\t\t\t\t_, err := bucket.Get(bearerToken[1], &amp;session)\r\n\t\t\t\tse err != nil {\r\n\t\t\t\t\tw.WriteHeader(401)\r\n\t\t\t\t\tw.Write([]byte(err.Error()))\r\n\t\t\t\t\treturn\r\n\t\t\t\t}\r\n\t\t\t\tcontext.Set(req, \"pid\", session.Pid)\r\n\t\t\t\tbucket.Touch(bearerToken[1], 0, 3600)\r\n\t\t\t\tnext(w, req)\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tw.WriteHeader(401)\r\n\t\t\tw.Write([]byte(\"\u00c9 necess\u00e1rio um cabe\u00e7alho de autoriza\u00e7\u00e3o\"))\r\n\t\t\treturn\r\n\t\t}\r\n\t})\r\n}<\/pre>\n<p>Toda solicita\u00e7\u00e3o a um de nossos tr\u00eas pontos de extremidade especiais exigir\u00e1 um cabe\u00e7alho de autoriza\u00e7\u00e3o que contenha um token de portador com o ID da sess\u00e3o. Sem token de portador significa que a solicita\u00e7\u00e3o falhar\u00e1. Um token de portador incorreto ou expirado significa que a solicita\u00e7\u00e3o falhar\u00e1.<\/p>\n<p>A valida\u00e7\u00e3o trocar\u00e1 o ID da sess\u00e3o por um ID do perfil a ser usado na pr\u00f3xima etapa da solicita\u00e7\u00e3o.<\/p>\n<p>Come\u00e7ando de forma simples, digamos que queremos retornar as informa\u00e7\u00f5es de perfil de um determinado usu\u00e1rio. Nosso endpoint pode ser parecido com o seguinte:<\/p>\n<pre class=\"lang:default decode:true\">func AccountEndpoint(w http.ResponseWriter, req *http.Request) {\r\n\tpid := context.Get(req, \"pid\").(string)\r\n\tvar profile Profile\r\n\t_, err := bucket.Get(pid, &amp;profile)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\tjson.NewEncoder(w).Encode(profile)\r\n}<\/pre>\n<p>O <code>pid<\/code> \u00e9 passado pelo middleware de valida\u00e7\u00e3o e uma pesquisa \u00e9 feita com o ID do perfil.<\/p>\n<p>At\u00e9 agora n\u00e3o foi t\u00e3o dif\u00edcil, certo?<\/p>\n<p>Vamos dar um passo adiante e introduzir algumas consultas N1QL em nosso projeto. Digamos que queiramos obter todas as publica\u00e7\u00f5es de blog de um determinado usu\u00e1rio. Isso usar\u00e1 o nosso \u00edndice e as consultas do tipo SQL.<\/p>\n<pre class=\"lang:default decode:true\">func BlogsEndpoint(w http.ResponseWriter, req *http.Request) {\r\n\tvar n1qlParams []interface{}\r\n\tn1qlParams = append(n1qlParams, context.Get(req, \"pid\").(string))\r\n\tquery := gocb.NewN1qlQuery(\"SELECT `\" + bucket.Name() + \"`.* FROM `\" + bucket.Name() + \"` WHERE type = 'blog' AND pid = $1\")\r\n\tconsulta.Consist\u00eancia(gocb.RequestPlus)\r\n\trows, err := bucket.ExecuteN1qlQuery(query, n1qlParams)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\tvar row Blog\r\n\tvar result []Blog\r\n\tfor rows.Next(&amp;row) {\r\n\t\tresult = append(result, row)\r\n\t\trow = Blog{}\r\n\t}\r\n\trows.Close()\r\n\tse result == nil {\r\n\t\tresult = make([]Blog, 0)\r\n\t}\r\n\tjson.NewEncoder(w).Encode(result)\r\n}<\/pre>\n<p>No c\u00f3digo do ponto de extremidade acima, pegamos o <code>pid<\/code> do middleware de valida\u00e7\u00e3o e adicion\u00e1-lo como um par\u00e2metro para nossa consulta parametrizada.<\/p>\n<p>Vamos iterar atrav\u00e9s do <code>Resultados da consulta<\/code> retornados da consulta e os adiciona a um <code>[]Blog<\/code> vari\u00e1vel. Se nenhum resultado for encontrado, podemos simplesmente retornar uma fatia vazia.<\/p>\n<p>Fazer pesquisas diretas com base na chave sempre ser\u00e1 mais r\u00e1pido do que as consultas N1QL, mas as consultas N1QL s\u00e3o muito \u00fateis quando voc\u00ea precisa consultar por informa\u00e7\u00f5es de propriedade.<\/p>\n<p>O ponto de chegada final n\u00e3o \u00e9 diferente do que j\u00e1 vimos:<\/p>\n<pre class=\"lang:default decode:true\">func BlogEndpoint(w http.ResponseWriter, req *http.Request) {\r\n\tvar blog Blog\r\n\t_ = json.NewDecoder(req.Body).Decode(&amp;blog)\r\n\tblog.Type = \"blog\"\r\n\tblog.Pid = context.Get(req, \"pid\").(string)\r\n\tblog.Timestamp = int(time.Now().Unix())\r\n\t_, err := bucket.Insert(uuid.NewV4().String(), blog, 0)\r\n\tse err != nil {\r\n\t\tw.WriteHeader(401)\r\n\t\tw.Write([]byte(err.Error()))\r\n\t\treturn\r\n\t}\r\n\tjson.NewEncoder(w).Encode(blog)\r\n}<\/pre>\n<p>No c\u00f3digo acima, aceitamos um corpo POST do cliente, bem como o <code>pid<\/code> do middleware de valida\u00e7\u00e3o. Essas informa\u00e7\u00f5es s\u00e3o ent\u00e3o salvas no Couchbase.<\/p>\n<h2>Conclus\u00e3o<\/h2>\n<p>Voc\u00ea acabou de ver como criar um armazenamento b\u00e1sico de perfil de usu\u00e1rio ou, neste caso, uma plataforma de blog, usando a linguagem de programa\u00e7\u00e3o Go e <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/\" target=\"_blank\" rel=\"noopener noreferrer\">Servidor Couchbase<\/a>. Este \u00e9 um tutorial alternativo para um <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/creating-user-profile-store-with-node-js-nosql-database\/\" target=\"_blank\" rel=\"noopener noreferrer\">tutorial anterior<\/a> que eu havia escrito sobre o mesmo assunto, mas com o Node.js.<\/p>\n<p>Deseja levar este tutorial para o pr\u00f3ximo n\u00edvel? Veja como criar um <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/creating-front-end-user-profile-store-angular-typescript\/\" target=\"_blank\" rel=\"noopener noreferrer\">front-end de cliente da Web com Angular<\/a> ou um <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/bringing-user-profile-store-mobile-nativescript-angular\/\" target=\"_blank\" rel=\"noopener noreferrer\">front-end de cliente m\u00f3vel com NativeScript<\/a>.<\/p>\n<p>Para obter mais informa\u00e7\u00f5es sobre como usar o Couchbase com a Golang, consulte a se\u00e7\u00e3o <a href=\"https:\/\/www.couchbase.com\/blog\/pt\/developers\/\" target=\"_blank\" rel=\"noopener noreferrer\">Portal do desenvolvedor do Couchbase<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>Lembra-se da s\u00e9rie de tutoriais que escrevi sobre a cria\u00e7\u00e3o de um armazenamento de perfil de usu\u00e1rio com Node.js e NoSQL? Esse tutorial abordou uma s\u00e9rie de assuntos, desde a cria\u00e7\u00e3o de uma API RESTful com o Node.js, manipula\u00e7\u00e3o de sess\u00f5es de usu\u00e1rio, modelagem de dados e, \u00e9 claro, [...]<\/p>","protected":false},"author":63,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,1815,1816,1820],"tags":[1393,1560,1572,1725,2019,2024],"ppma_author":[9032],"class_list":["post-3901","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-best-practices-and-tutorials","category-couchbase-server","category-golang","tag-api","tag-bcrypt","tag-database","tag-nosql-database","tag-profile-store","tag-session"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Golang + NoSQL Database Management for User Profile Store<\/title>\n<meta name=\"description\" content=\"This Couchbase post shows how to develop a user profile store with Golang and Couchbase Server acting as a modular replacement to the Node.js alternative.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/developing-user-profile-store-golang-nosql-database\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Developing a User Profile Store with Golang and a NoSQL Database\" \/>\n<meta property=\"og:description\" content=\"This Couchbase post shows how to develop a user profile store with Golang and Couchbase Server acting as a modular replacement to the Node.js alternative.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/developing-user-profile-store-golang-nosql-database\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/thepolyglotdeveloper\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-29T14:00:39+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T01:46:00+00:00\" \/>\n<meta name=\"author\" content=\"Nic Raboy, Developer Advocate, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@nraboy\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nic Raboy, Developer Advocate, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/\"},\"author\":{\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1\"},\"headline\":\"Developing a User Profile Store with Golang and a NoSQL Database\",\"datePublished\":\"2017-08-29T14:00:39+00:00\",\"dateModified\":\"2025-06-14T01:46:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/\"},\"wordCount\":1360,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"keywords\":[\"API\",\"bcrypt\",\"database\",\"NoSQL Database\",\"profile store\",\"session\"],\"articleSection\":[\"Application Design\",\"Best Practices and Tutorials\",\"Couchbase Server\",\"GoLang\"],\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/\",\"name\":\"Golang + NoSQL Database Management for User Profile Store\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2017-08-29T14:00:39+00:00\",\"dateModified\":\"2025-06-14T01:46:00+00:00\",\"description\":\"This Couchbase post shows how to develop a user profile store with Golang and Couchbase Server acting as a modular replacement to the Node.js alternative.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Developing a User Profile Store with Golang and a NoSQL Database\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1\",\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/8863514d8bed0cf6080f23db40e00354\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g\",\"caption\":\"Nic Raboy, Developer Advocate, Couchbase\"},\"description\":\"Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.\",\"sameAs\":[\"https:\/\/www.thepolyglotdeveloper.com\",\"https:\/\/www.facebook.com\/thepolyglotdeveloper\",\"https:\/\/x.com\/nraboy\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/pt\/author\/nic-raboy-2\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Golang + gerenciamento de banco de dados NoSQL para armazenamento de perfil de usu\u00e1rio","description":"Esta postagem do Couchbase mostra como desenvolver um armazenamento de perfil de usu\u00e1rio com o Golang e o Couchbase Server, atuando como um substituto modular para a alternativa do Node.js.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/pt\/developing-user-profile-store-golang-nosql-database\/","og_locale":"pt_BR","og_type":"article","og_title":"Developing a User Profile Store with Golang and a NoSQL Database","og_description":"This Couchbase post shows how to develop a user profile store with Golang and Couchbase Server acting as a modular replacement to the Node.js alternative.","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/developing-user-profile-store-golang-nosql-database\/","og_site_name":"The Couchbase Blog","article_author":"https:\/\/www.facebook.com\/thepolyglotdeveloper","article_published_time":"2017-08-29T14:00:39+00:00","article_modified_time":"2025-06-14T01:46:00+00:00","author":"Nic Raboy, Developer Advocate, Couchbase","twitter_card":"summary_large_image","twitter_creator":"@nraboy","twitter_misc":{"Written by":"Nic Raboy, Developer Advocate, Couchbase","Est. reading time":"6 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/"},"author":{"name":"Nic Raboy, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1"},"headline":"Developing a User Profile Store with Golang and a NoSQL Database","datePublished":"2017-08-29T14:00:39+00:00","dateModified":"2025-06-14T01:46:00+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/"},"wordCount":1360,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","keywords":["API","bcrypt","database","NoSQL Database","profile store","session"],"articleSection":["Application Design","Best Practices and Tutorials","Couchbase Server","GoLang"],"inLanguage":"pt-BR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/","url":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/","name":"Golang + gerenciamento de banco de dados NoSQL para armazenamento de perfil de usu\u00e1rio","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2017-08-29T14:00:39+00:00","dateModified":"2025-06-14T01:46:00+00:00","description":"Esta postagem do Couchbase mostra como desenvolver um armazenamento de perfil de usu\u00e1rio com o Golang e o Couchbase Server, atuando como um substituto modular para a alternativa do Node.js.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/developing-user-profile-store-golang-nosql-database\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Developing a User Profile Store with Golang and a NoSQL Database"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"Blog do Couchbase","description":"Couchbase, o banco de dados NoSQL","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"Blog do Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1","name":"Nic Raboy, defensor dos desenvolvedores, Couchbase","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/8863514d8bed0cf6080f23db40e00354","url":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","caption":"Nic Raboy, Developer Advocate, Couchbase"},"description":"Nic Raboy \u00e9 um defensor das modernas tecnologias de desenvolvimento m\u00f3vel e da Web. Ele tem experi\u00eancia em Java, JavaScript, Golang e uma variedade de estruturas, como Angular, NativeScript e Apache Cordova. Nic escreve sobre suas experi\u00eancias de desenvolvimento relacionadas a tornar o desenvolvimento m\u00f3vel e da Web mais f\u00e1cil de entender.","sameAs":["https:\/\/www.thepolyglotdeveloper.com","https:\/\/www.facebook.com\/thepolyglotdeveloper","https:\/\/x.com\/nraboy"],"url":"https:\/\/www.couchbase.com\/blog\/pt\/author\/nic-raboy-2\/"}]}},"authors":[{"term_id":9032,"user_id":63,"is_guest":0,"slug":"nic-raboy-2","display_name":"Nic Raboy, Developer Advocate, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","author_category":"","last_name":"Raboy","first_name":"Nic","job_title":"","user_url":"https:\/\/www.thepolyglotdeveloper.com","description":"Nic Raboy \u00e9 um defensor das modernas tecnologias de desenvolvimento m\u00f3vel e da Web. Ele tem experi\u00eancia em Java, JavaScript, Golang e uma variedade de estruturas, como Angular, NativeScript e Apache Cordova. Nic escreve sobre suas experi\u00eancias de desenvolvimento relacionadas a tornar o desenvolvimento m\u00f3vel e da Web mais f\u00e1cil de entender."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/3901","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=3901"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/3901\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=3901"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=3901"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=3901"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=3901"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}