{"id":520415,"date":"2024-11-04T10:53:17","date_gmt":"2024-11-04T09:53:17","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=go&#038;p=520415"},"modified":"2025-10-16T11:31:43","modified_gmt":"2025-10-16T10:31:43","slug":"create-a-full-stack-app-with-go-and-react","status":"publish","type":"go","link":"https:\/\/blog.jetbrains.com\/en\/go\/2024\/11\/04\/create-a-full-stack-app-with-go-and-react","title":{"rendered":"Create a Full-Stack App With Go and React"},"content":{"rendered":"\n<p>As a language that emerged over 14 years ago, Go has many use cases. From web development, APIs, and CLIs to Wasm, cloud technologies, and even AI-powered tools, its applications are broad.&nbsp;<\/p>\n\n\n\n<p>The same goes for demo projects, which have countless variations and purposes!<\/p>\n\n\n\n<p>Now, wouldn\u2019t it be great if there was a full-stack, API-oriented, and well-maintained demo project for Go? If you\u2019ve ever experienced a demo project fail during a live presentation, then you\u2019d certainly appreciate a more stable project!&nbsp;<\/p>\n\n\n\n<p>In this blog post, I\u2019ll walk you through the process of creating such a project \u2013 a full-stack app using Go and React.<br><br>Check out the source code <a href=\"https:\/\/github.com\/mukulmantosh\/Go_Food_Delivery\" target=\"_blank\" rel=\"noopener\">here<\/a> to follow along.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introducing Go Eats<\/h2>\n\n\n\n<p>Go Eats is an open-source project similar to the food delivery apps you\u2019ve probably seen and used before. The project simulates the operations involved when placing an order in a real-world food delivery app.<\/p>\n\n\n\n<p>To build Go Eats, I used Go as the programming language, Postgres as the database, React for the frontend, and NATS for messaging. While Go is a good choice for these complex full-stack applications, finding demos can be challenging.<\/p>\n\n\n\n<p>I used <a href=\"https:\/\/www.jetbrains.com\/go\/\" target=\"_blank\" rel=\"noopener\">GoLand<\/a> for this project because it takes care of setting up the Go SDK, installing packages, and much more straight out of the box.<\/p>\n\n\n\n<p>Let\u2019s take a closer look at the entry point of the source code: <em>main.go<\/em>. <\/p>\n\n\n\n<p>This code initializes several services and handlers, establishes a database connection, loads environment variables, and sets up middleware, NATS, and the WebSocket.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">func main() {\n\t\/\/ load .env file\n\terr := godotenv.Load()\n\tif err != nil {\n\t\tlog.Fatal(&quot;Error loading .env file&quot;)\n\t}\n\n\tenv := os.Getenv(&quot;APP_ENV&quot;)\n\tdb := database.New()\n\t\/\/ Create Tables\n\tif err := db.Migrate(); err != nil {\n\t\tlog.Fatalf(&quot;Error migrating database: %s&quot;, err)\n\t}\n\n\t\/\/ Connect NATS\n\tnatServer, err := nats.NewNATS(&quot;nats:\/\/127.0.0.1:4222&quot;)\n\n\t\/\/ WebSocket Clients\n\twsClients := make(map&#091;string]*websocket.Conn)\n\n\ts := handler.NewServer(db, true)\n\n\t\/\/ Initialize Validator\n\tvalidate := validator.New()\n\n\t\/\/ Middlewares List\n\tmiddlewares := &#091;]gin.HandlerFunc{middleware.AuthMiddleware()}\n\n\t\/\/ User\n\tuserService := usr.NewUserService(db, env)\n\tuser.NewUserHandler(s, &quot;\/user&quot;, userService, validate)\n\n\t\/\/ Restaurant\n\trestaurantService := restro.NewRestaurantService(db, env)\n\trestaurant.NewRestaurantHandler(s, &quot;\/restaurant&quot;, restaurantService)\n\n\t\/\/.... For the sake of brevity, the full code snippet is omitted here.\n\n\tlog.Fatal(s.Run())\n\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">The journey<\/h2>\n\n\n\n<p>I started by working on backend tasks, such as creating APIs, before moving to the frontend. I chose <a href=\"https:\/\/gin-gonic.com\/\" target=\"_blank\" rel=\"noopener\">Gin<\/a> as the framework to build these APIs.<\/p>\n\n\n\n<p>While I could have gone with <a href=\"https:\/\/gorilla.github.io\/\" target=\"_blank\" rel=\"noopener\">Gorilla<\/a>, <a href=\"https:\/\/echo.labstack.com\/\" target=\"_blank\" rel=\"noopener\">Echo<\/a>, <a href=\"https:\/\/docs.gofiber.io\/\" target=\"_blank\" rel=\"noopener\">Fiber<\/a>, or any other framework, I felt Gin would be a better choice for this project. It\u2019s more mature and stable, it\u2019s widely used, and it\u2019s also somewhat of a personal preference.&nbsp;<\/p>\n\n\n\n<p>Before selecting any framework, you should carefully consider the problem you are trying to solve or the specific business goal you are trying to achieve. Then weigh up the potential solutions and opt for the best one for your specific use case.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The backend<\/h3>\n\n\n\n<p>While creating a demo app, it\u2019s important to ensure that the application is flexible enough to accommodate future changes and refactoring.<\/p>\n\n\n\n<p>For Go Eats, I implemented a service layer pattern, along with <a href=\"https:\/\/www.freecodecamp.org\/news\/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f\/\" target=\"_blank\" rel=\"noopener\">dependency injection<\/a>. Additionally, I utilized handlers for request and response communication with the service layer, which manages business logic and interacts with the database.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">\t\/\/ User\n\tuserService := usr.NewUserService(db, env)\n\tuser.NewUserHandler(s, &quot;\/user&quot;, userService, validate)\n\n\t\/\/ Restaurant\n\trestaurantService := restro.NewRestaurantService(db, env)\n\trestaurant.NewRestaurantHandler(s, &quot;\/restaurant&quot;, restaurantService)\n\n\t\/\/ Reviews\n\treviewService := review.NewReviewService(db, env)\n\trevw.NewReviewProtectedHandler(s, &quot;\/review&quot;, reviewService, middlewares, validate)\n\n\t\/\/ Cart\n\tcartService := cart_order.NewCartService(db, env, natServer)\n\tcrt.NewCartHandler(s, &quot;\/cart&quot;, cartService, middlewares, validate)<\/pre>\n\n\n\n<p>The project structure shown above ensures that the project is modular and flexible. Remember that creating too many nested directories can add unnecessary complexity. Especially if you\u2019re just starting your programming journey, it\u2019s best to focus on simplicity rather than going through multiple layers. Keep in mind that your code will inevitably need to be refactored at some point.<\/p>\n\n\n\n<p>The famous quote by Donald Knuth, \u201c<em>Premature optimization is the root of all evil<\/em>,\u201d<em> <\/em>feels particularly apt here.<\/p>\n\n\n\n<p><img decoding=\"async\" loading=\"lazy\" width=\"624\" height=\"796\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/AD_4nXfcI9krAp38XYuPrQNLmoodAc9NYK0eUZM96CeYke7JsRmpRYgddqH_YkmjJJbzbYOMsgvBqnD9o7Nm57hzq0X20EYsvTo2m23VqJApEXIW8MibVHyyHKDJQaW_NV1TJIEIJhsc9zGAX18p1eZMlWNgoJpq.png\"><\/p>\n\n\n\n<p>I did something similar for the database. I used the lightweight <a href=\"https:\/\/bun.uptrace.dev\/\" target=\"_blank\" rel=\"noopener\">Bun<\/a> SQL client.&nbsp;<\/p>\n\n\n\n<p>However, I made sure that if I need to replace the <a href=\"https:\/\/www.freecodecamp.org\/news\/what-is-an-orm-the-meaning-of-object-relational-mapping-database-tools\/\" target=\"_blank\" rel=\"noopener\">ORM<\/a> in the future, my code will be able to adapt to the new changes based on the interface I have defined below.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">type Database interface {\n\tInsert(ctx context.Context, model any) (sql.Result, error)\n\tDelete(ctx context.Context, tableName string, filter Filter) (sql.Result, error)\n\tSelect(ctx context.Context, model any, columnName string, parameter any) error\n\tSelectAll(ctx context.Context, tableName string, model any) error\n\tSelectWithRelation(ctx context.Context, model any, relations &#091;]string, Condition Filter) error\n\tSelectWithMultipleFilter(ctx context.Context, model any, Condition Filter) error\n\tRaw(ctx context.Context, model any, query string, args ...interface{}) error\n\tUpdate(ctx context.Context, tableName string, Set Filter, Condition Filter) (sql.Result, error)\n\tCount(ctx context.Context, tableName string, ColumnExpression string, columnName string, parameter any) (int64, error)\n\tMigrate() error\n\tHealthCheck() bool\n\tClose() error\n}<\/pre>\n\n\n\n<p>This interface provides a blueprint for CRUD (create, read, update, and delete) operations and other common database interactions, ensuring that any <strong>struct<\/strong> implementing this interface can be used to perform these operations.<\/p>\n\n\n\n<p>GoLand keeps track of <a href=\"https:\/\/www.jetbrains.com\/help\/go\/navigating-through-the-source-code.html#go_to_implementation\" target=\"_blank\" rel=\"noopener\">class implementation<\/a>, making navigation much smoother.&nbsp;<\/p>\n\n\n\n<p>To navigate to the implementation, press <em>\u2318\u2325B \/ Ctrl+Alt+B<\/em>.<\/p>\n\n\n\n<p><img decoding=\"async\" loading=\"lazy\" width=\"624\" height=\"248\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXcLVgdXRA787wh3wVWZeEfa0MHO5jidVloVzoTPbCI2uBPFLWR3kg_HysAYf0BiiMuJw7pTBiveo1yikbe3LQD9oHeH23NNxfo3_ikgepSiDXc4q_nfQ-x5_2fBvSBVYlWBCF6pJH3FKv-1oWx-kQS3SQYL?key=hKmNGEQb3WqmXmU-JHx7WQ\"><\/p>\n\n\n\n<p>Most of the operations involve CRUD activities. I was also eager to experiment with server-sent events (<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Server-sent_events\/Using_server-sent_events\" target=\"_blank\" rel=\"noopener\">SSE<\/a>) and <a href=\"https:\/\/www.youtube.com\/watch?v=8ARodQ4Wlf4\" target=\"_blank\" rel=\"noopener\">WebSockets<\/a>, which I\u2019ve not explored much in the past. This project was a good use case for these technologies.<\/p>\n\n\n\n<p>Let\u2019s begin with SSE, a technology that enables the server to push notifications, messages, and events to clients over an HTTP connection.<\/p>\n\n\n\n<p>The code snippet below reads data from a JSON file and pushes the information so the client renders and displays the relevant data.&nbsp;<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">func (s *AnnouncementHandler) flashNews(c *gin.Context) {\n\t_, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)\n\tdefer cancel()\n\n\tevents, err := s.service.FlashEvents()\n\tif err != nil {\n\t\tc.AbortWithStatusJSON(500, gin.H{&quot;error&quot;: err.Error()})\n\t\treturn\n\t}\n\n\t\/\/ Set headers for SSE\n\tc.Header(&quot;Content-Type&quot;, &quot;text\/event-stream&quot;)\n\tc.Header(&quot;Cache-Control&quot;, &quot;no-cache&quot;)\n\tc.Header(&quot;Connection&quot;, &quot;keep-alive&quot;)\n\n\tticker := time.NewTicker(6 * time.Second)\n\tdefer ticker.Stop()\n\n\teventIndex := 0\n\n\tfor {\n\t\tselect {\n\t\tcase &lt;-ticker.C:\n\t\t\t\/\/ Send the current event\n\t\t\tevent := (*events)&#091;eventIndex]\n\t\t\tc.SSEvent(&quot;message&quot;, event.Message)\n\t\t\tc.Writer.Flush()\n\n\t\t\t\/\/ Move to the next event\n\t\t\teventIndex = (eventIndex + 1) % len(*events)\n\t\tcase &lt;-c.Request.Context().Done():\n\t\t\tticker.Stop()\n\t\t\treturn\n\t\t}\n\t}\n\n}<\/pre>\n\n\n\n<figure class=\"wp-block-image size-full is-style-default\"><img decoding=\"async\" loading=\"lazy\" width=\"1592\" height=\"720\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/10\/flash_event.gif\" alt=\"\" class=\"wp-image-521105\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">WebSocket and NATS<\/h3>\n\n\n\n<p>Something else I&#8217;ve not worked with before but have always wanted to try is using WebSockets with the NATS messaging system. I evaluated these technologies as potential solutions for the project and ultimately decided to use them in the demo app.<\/p>\n\n\n\n<p>Here&#8217;s how the messaging process in the Go Eats app functions:&nbsp;<\/p>\n\n\n\n<p>Whenever a new order is created, a message is published to NATS informing the subscriber about the newly placed order.<\/p>\n\n\n\n<p>When the delivery person updates the order \u2013 whether it&#8217;s on the way, has failed, or has been delivered \u2013 the customer will receive this information in real time, a process that will be reflected in the UI.<\/p>\n\n\n\n<p><img decoding=\"async\" loading=\"lazy\" width=\"624\" height=\"884\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXcWyRES0O3V63Gl1cguEmo_rkywJVpzNlYujN4n-gPATfy2wvdndIKxNOYhQZEpzrNOPME_M-b5EmOiGKQ4-tVLbZXOj5sWeyALEgqPQAlDOCl7EA6YSbkBbT-TM9mKch1t2WlrMLo4S6uhLeLrstlbBa0t?key=hKmNGEQb3WqmXmU-JHx7WQ\"><\/p>\n\n\n\n<p>The code below defines the two topics:<\/p>\n\n\n\n<ul>\n<li><strong>orders.new.*<\/strong>: Sends a notification to the customer when a new order is placed.<\/li>\n\n\n\n<li><strong>orders.status.*<\/strong>: Sends a notification to the customer when the delivery status is updated.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"969\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2024\/11\/image-2.png\" alt=\"\" class=\"wp-image-523135\"\/><\/figure>\n\n\n\n<p>For each received message, the application logs the message, extracts the user ID and message data, and attempts to send the data to the corresponding WebSocket client. If sending fails, the app logs the error, closes the WebSocket connection, and removes the client from the active client&#8217;s map.<\/p>\n\n\n\n<p>Note: Using <strong>map[string]*websocket.Conn<\/strong> to manage WebSocket connections with keys as client identifiers, such as userID, will not handle multiple connections from the same client, as the map keys must be unique. This is something I am planning to expand on in a future blog post.&nbsp;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why NATS over Kafka?<\/h3>\n\n\n\n<p>Kafka is capable of handling large amounts of data, offering database-level durability and being enterprise-ready.&nbsp;<\/p>\n\n\n\n<p>However, I have never used it before, and I needed something that would work well for my specific use case. In that context, I came across <a href=\"https:\/\/nats.io\/\" target=\"_blank\" rel=\"noopener\">NATS<\/a>, which is written in Go and is ideal for lightweight, low-latency messaging, particularly in microservices and cloud-native architectures.&nbsp;<\/p>\n\n\n\n<p>A key point I would like to highlight is its simplicity. NATS was easy to use even when just starting to work with it.<\/p>\n\n\n\n<p>The code below initializes a NATS connection and defines methods to publish and subscribe to messages. It connects to a NATS server, publishes messages to a topic, and forwards received messages to WebSocket clients based on user IDs.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">type NATS struct {\n\tConn *nats.Conn\n}\n\nfunc NewNATS(url string) (*NATS, error) {\n\tnc, err := nats.Connect(url, nats.Name(&quot;food-delivery-nats&quot;))\n\tif err != nil {\n\t\tlog.Fatalf(&quot;Error connecting to NATS:: %s&quot;, err)\n\t}\n\treturn &amp;NATS{Conn: nc}, err\n}\n\nfunc (n *NATS) Pub(topic string, message &#091;]byte) error {\n\terr := n.Conn.Publish(topic, message)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (n *NATS) Sub(topic string, clients map&#091;string]*websocket.Conn) error {\n\n\t_, err := n.Conn.Subscribe(topic, func(msg *nats.Msg) {\n\t\tmessage := string(msg.Data)\n\t\tslog.Info(&quot;MESSAGE_REPLY_FROM_NATS&quot;, &quot;RECEIVED_MESSAGE&quot;, message)\n\t\tuserId, messageData := n.formatMessage(message)\n\t\tif conn, ok := clients&#091;userId]; ok {\n\t\t\terr := conn.WriteMessage(websocket.TextMessage, &#091;]byte(messageData))\n\t\t\tif err != nil {\n\t\t\t\tlog.Println(&quot;Error sending message to client:&quot;, err)\n\t\t\t\tconn.Close()\n\t\t\t\tdelete(clients, userId)\n\t\t\t}\n\t\t}\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n\/\/.... For the sake of brevity, the full code snippet is omitted here.<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">The frontend<\/h3>\n\n\n\n<p>To build the Go Eats UI, I used React. I don\u2019t come from a frontend background, and it was quite challenging to learn and apply unfamiliar concepts. Along the way, I took Stephen Grider&#8217;s <em>Modern React with Redux <\/em><a href=\"https:\/\/www.udemy.com\/course\/react-redux\/\" target=\"_blank\" rel=\"noopener\">course<\/a>.<\/p>\n\n\n\n<p>While I\u2019m certainly no expert in React, the course helped a lot. That said, it was the use of GoLand, the JetBrains IDE for Go, which proved to be most valuable throughout this process.<\/p>\n\n\n\n<p>GoLand has excellent support for <a href=\"https:\/\/www.jetbrains.com\/go\/features\/#front-end-and-back-end-development\" target=\"_blank\" rel=\"noopener\">full-stack<\/a> development with less distraction and context switching. The IDE provides exceptional coding support for JavaScript, TypeScript, Dart, React, and many other languages.<\/p>\n\n\n\n<p>Using GoLand, I can develop frontend and backend applications with the same IDE.<\/p>\n\n\n\n<p><img decoding=\"async\" loading=\"lazy\" width=\"624\" height=\"351\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/10\/unnamed-28.png\"><\/p>\n\n\n\n<p>Finally, here is the final output of how the UI gets rendered through React.&nbsp;<\/p>\n\n\n\n<p><img decoding=\"async\" loading=\"lazy\" width=\"624\" height=\"305\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXcemADU4Q-EYg461wCT3rPR-YYOA3QyHnUDIXRO9LnnsZRG8YgcGcenXXjACRQWcmAJysRUE4PbUBw_AnOCuGCy0Gwl09-23WqewIvXFPj_kNrIZzQ722isiFgn81buHmIVn-RHi_2rmaU0ELXCZO9lJziI?key=hKmNGEQb3WqmXmU-JHx7WQ\"><\/p>\n\n\n\n<p>I haven\u2019t gone into much detail about the process of developing the Go Eats frontend. However, if you\u2019re interested, you can check out the source code <a href=\"https:\/\/github.com\/mukulmantosh\/food_delivery_frontend\" target=\"_blank\" rel=\"noopener\">here<\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>As I reflect on my journey developing this demo app, I realize there is much to learn and many improvements that could be made.<\/p>\n\n\n\n<p>Here are some key lessons from this experience:<\/p>\n\n\n\n<ul>\n<li>Focus on functionality first!<br>If you\u2019re someone who is just starting to work with Go, don\u2019t worry about making your app scalable or more modular by focusing on dependency injection right away. These concepts will eventually be required, and you will observe the patterns and restructure accordingly. Whatever you build, it won\u2019t be perfect at first, and that\u2019s okay! The journey is one of incremental and constant improvements.&nbsp;<br><\/li>\n\n\n\n<li>Prioritize testing!<br>Don\u2019t forget the importance of regular testing. Making sure you test your project frequently provides confidence that your features work as expected. While I have worked on tests for Go Eats, I still need to improve them further. Additionally, I have tried different testing libraries, such as <a href=\"https:\/\/testcontainers.com\/\" target=\"_blank\" rel=\"noopener\">Testcontainers<\/a>, which help me test features in a real database instead of using mock ones.<br><\/li>\n\n\n\n<li>Database design matters!<br>Spending time on the database schema is extremely valuable. Additionally, there are database migrations, which I haven\u2019t used, but they are quite important, especially when you\u2019re looking for rollbacks, versioning changes, and ensuring that changes are applied in the correct order.<br><\/li>\n<\/ul>\n\n\n\n<p>While I\u2019m happy with what I\u2019ve built so far, it\u2019s just the beginning for Go Eats. Stay tuned for more updates soon!&nbsp;<\/p>\n\n\n\n<p>I\u2019d also love to hear your feedback. If you\u2019ve had similar experiences or if you have thoughts on what you liked or disliked, or if you have suggestions for improvement, please share them in the comments.<\/p>\n","protected":false},"author":1398,"featured_media":521117,"comment_status":"closed","ping_status":"closed","template":"","categories":[2347],"tags":[8612,4197,4189,1290],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go\/520415"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/go"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/1398"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=520415"}],"version-history":[{"count":9,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go\/520415\/revisions"}],"predecessor-version":[{"id":650651,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go\/520415\/revisions\/650651"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media\/521117"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=520415"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=520415"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=520415"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=520415"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}