{"id":34955948,"url":"https://github.com/stevenstr/web-service-gin","last_synced_at":"2026-05-19T23:32:15.545Z","repository":{"id":297389891,"uuid":"996548278","full_name":"stevenstr/web-service-gin","owner":"stevenstr","description":null,"archived":false,"fork":false,"pushed_at":"2025-06-05T07:24:29.000Z","size":11723,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-06-05T08:36:35.875Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevenstr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-05T05:34:19.000Z","updated_at":"2025-06-05T07:24:32.000Z","dependencies_parsed_at":"2025-06-05T08:47:27.578Z","dependency_job_id":null,"html_url":"https://github.com/stevenstr/web-service-gin","commit_stats":null,"previous_names":["stevenstr/web-service-gin"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/stevenstr/web-service-gin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenstr%2Fweb-service-gin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenstr%2Fweb-service-gin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenstr%2Fweb-service-gin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenstr%2Fweb-service-gin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevenstr","download_url":"https://codeload.github.com/stevenstr/web-service-gin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenstr%2Fweb-service-gin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28062354,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-26T02:00:06.189Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-26T22:10:46.023Z","updated_at":"2025-12-26T22:10:46.701Z","avatar_url":"https://github.com/stevenstr.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# web-service-gin\n\nDeveloping a RESTful API with Go and Gin\nThis tutorial introduces the basics of writing a RESTful web service API with Go and the Gin Web Framework (Gin).\n\nYou'll get the most out of this tutorial if you have a basic familiarity with Go and its tooling. If this is your first exposure to Go, please see Tutorial: Get started with Go for a quick introduction.\n\nGin simplifies many coding tasks associated with building web applications, including web services. In this tutorial, you'll use Gin to route requests, retrieve request details, and marshal JSON for responses.\n\nIn this tutorial, you will build a RESTful API server with two endpoints. Your example project will be a repository of data about vintage jazz records.\n\nThe tutorial includes the following sections:\n\n- Design API endpoints.\n- Create a folder for your code.\n- Create the data.\n- Write a handler to return all items.\n- Write a handler to add a new item.\n- Write a handler to return a specific item.\n- Design API endpoints\nYou'll build an API that provides access to a store selling vintage recordings on vinyl. So you'll need to provide endpoints through which a client can get and add albums for users.\n\nWhen 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.\n\nHere are the endpoints you'll create in this tutorial.\n```sh\n/albums\n```\nGET – Get a list of all albums, returned as JSON.\nPOST – Add a new album from request data sent as JSON.\n\n```sh\n/albums/:id\n```\nGET – Get an album by its ID, returning the album data as JSON.\nNext, you'll create a project for your code.\n\nhttps://shell.cloud.google.com/?walkthrough_tutorial_url=https%3A%2F%2Fraw.githubusercontent.com%2Fgolang%2Ftour%2Fmaster%2Ftutorial%2Fweb-service-gin.md\u0026pli=1\u0026show=ide\u0026environment_deployment=ide\n\n# Step 1\n## Step 1 of 7\nCreate a project for your code\nTo begin, create a project for the code you'll write.\n\nEnsure that the cloudshell_open folder is selected.\n\nClick the File Menu, then click New Folder.\n\nIn the New Folder dialog, enter web-service-gin for the folder name, then click OK.\n\nClick the File Menu, then click Open Workspace.\n\nIn the Open Workspace dialog, select the cloudshell_open/web-service-gin folder you just created, then click Open.\n\nClick the New Terminal menu command to open a new Cloud Shell terminal.\n\nThe terminal prompt should be in the web-service-gin directory.\n\nCreate a module in which you can manage dependencies.\n\nRun the go mod init command, giving it the path of the module your code will be in.\n```sh\ngo mod init \\\n    example.com/web-service-gin\ngo: creating new go.mod: module \\\n    example.com/web-service-gin\n```\nThis command creates a go.mod file in which dependencies you might add will be listed for tracking. For more, be sure to see Managing dependencies.\n\nNext, you'll design data structures for handling data.\n\n\n# Step 2\n## Step 2 of 7\nCreate the data\nTo keep things simple for the tutorial, you'll store data in memory. A more typical API would interact with a database.\n\nNote that storing data in memory means that the set of albums will be lost each time you stop the server, then recreated when you start it.\n\nWrite the code\nClick the File Menu, then click New File.\n\nIn the New File dialog, enter main.go for the file name, then click OK.\n\nInto main.go, at the top of the file, paste the package declaration below.\n```Go\npackage main\n```\nA standalone program (as opposed to a library) is always in package main.\n\nBeneath the package declaration, paste the following declaration of an album struct. You'll use this to store album data in memory.\n\nStruct tags such as json:\"artist\" specify what a field's name should be when the struct's contents are serialized into JSON. Without them, the JSON would use the struct's capitalized field names – a style not as common in JSON.\n```Go\n// album represents data about a record album.\ntype album struct {\n        ID     string  `json:\"id\"`\n        Title  string  `json:\"title\"`\n        Artist string  `json:\"artist\"`\n        Price  float64 `json:\"price\"`\n}\n```\nBeneath the struct declaration you just added, paste the following slice of album structs containing data you'll use to start.\n\n```Go\n// albums slice to seed record album data.\nvar albums = []album{\n        {ID: \"1\", Title: \"Blue Train\", Artist: \"John Coltrane\", Price: 56.99},\n        {ID: \"2\", Title: \"Jeru\", Artist: \"Gerry Mulligan\", Price: 17.99},\n        {ID: \"3\", Title: \"Sarah Vaughan and Clifford Brown\", Artist: \"Sarah Vaughan\", Price: 39.99},\n}\n```\nNext, you'll write code to implement your first endpoint.\n\n# Step 3\n## Step 3 of 7\nWrite a handler to return all items\nWhen the client makes a request at GET /albums, you want to return all the albums as JSON.\n\nTo do this, you'll write the following:\n- Logic to prepare a response\n- Code to map the request path to your logic\nNote that this is the reverse of how they'll be executed at runtime, but you're adding dependencies first, then the code that depends on them.\n\n### Write the code\nAs you follow these steps, ignore the errors visible in the editor. You'll fix these in Run the code, below.\n\n1. Beneath the struct code you added in the preceding section, paste the following code to get the album list.\n\nThis getAlbums function creates JSON from the slice of album structs, writing the JSON into the response.\n```Go\n// getAlbums responds with the list of all albums as JSON.\nfunc getAlbums(c *gin.Context) {\n        c.IndentedJSON(http.StatusOK, albums)\n}\n```\nIn this code, you:\n\n- Write a getAlbums function that takes a gin.Context parameter. Note that you could have given this function any name – neither Gin nor Go require a particular function name format.\n\ngin.Context is the most important part of Gin. It carries request details, validates and serializes JSON, and more. (Despite the similar name, this is different from Go's built-in context package.)\n\n- Call Context.IndentedJSON to serialize the struct into JSON and add it to the response.\n\nThe function's first argument is the HTTP status code you want to send to the client. Here, you're passing the StatusOK constant from the net/http package to indicate 200 OK.\n\nNote that you can replace Context.IndentedJSON with a call to Context.JSON to send more compact JSON. In practice, the indented form is much easier to work with when debugging and the size difference is usually small.\n\n2. Near the top of main.go, just beneath the albums slice declaration, paste the code below to assign the handler function to an endpoint path.\n\nThis sets up an association in which getAlbums handles requests to the /albums endpoint path.\n```Go\nfunc main() {\n        router := gin.Default()\n        router.GET(\"/albums\", getAlbums)\n\n        router.Run(\"localhost:8080\")\n}\n```\nIn this code, you:\n\n- Initialize a Gin router using Default.\n\n- Use the GET function to associate the GET HTTP method and /albums path with a handler function.\n\nNote that you're passing the name of the getAlbums function. This is different from passing the result of the function, which you would do by passing getAlbums() (note the parenthesis).\n\n- Use the Run function to attach the router to an http.Server and start the server.\n\n3. Near the top of main.go, just beneath the package declaration, import the packages you'll need to support the code you've just written.\n\nThe first lines of code should look like this:\n```Go\npackage main\n\nimport (\n        \"net/http\"\n\n        \"github.com/gin-gonic/gin\"\n)\n```\n4. Save main.go.\n\n### Run the code\n1. Begin tracking the Gin module as a dependency.\n\nAt the command line, use go get to add the github.com/gin-gonic/gin module as a dependency for your module. Use a dot argument to mean \"get dependencies for code in the current directory.\"\n\n```sh\ngo get .\ngo get: added \\\n    github.com/gin-gonic/gin v1.7.2\n```\nGo resolved and downloaded this dependency to satisfy the import declaration you added in the previous step.\n\n2. In the Cloud Shell terminal window, run the code.\n\nUse a dot argument to mean \"run the package in the current directory.\"\n```sh\ngo run .\n```\nOnce the code is running, you have a running HTTP server to which you can send requests.\n\n3. Open a new terminal window in which to make requests to the running web service.\n\nClick the New Terminal menu command to open a new Cloud Shell terminal.\n\n4. In the second Cloud Shell terminal window, use curl to make a request to your running web service.\n```sh\ncurl http://localhost:8080/albums\n```\nThe command should display the data you seeded the service with.\n```json\n[\n        {\n                \"id\": \"1\",\n                \"title\": \"Blue Train\",\n                \"artist\": \"John Coltrane\",\n                \"price\": 56.99\n        },\n        {\n                \"id\": \"2\",\n                \"title\": \"Jeru\",\n                \"artist\": \"Gerry Mulligan\",\n                \"price\": 17.99\n        },\n        {\n                \"id\": \"3\",\n                \"title\": \"Sarah Vaughan and Clifford Brown\",\n                \"artist\": \"Sarah Vaughan\",\n                \"price\": 39.99\n        }\n]\n```\nYou've started an API! In the next section, you'll create another endpoint with code to handle a POST request to add an item.   \n\n\n# Step 4\n## Step 4 of 7\n### Write a handler to add a new item\nWhen the client makes a POST request at /albums, you want to add the album described in the request body to the existing albums data.\n\nTo do this, you'll write the following:\n\n- Logic to add the new album to the existing list.\n- A bit of code to route the POST request to your logic.\n\n### Write the code\n1. Add code to add albums data to the list of albums.\n\nSomewhere after the import statements, paste the following code. (The end of the file is a good place for this code, but Go doesn't enforce the order in which you declare functions.)\n```Go\n// postAlbums adds an album from JSON received in the request body.\nfunc postAlbums(c *gin.Context) {\n        var newAlbum album\n\n        // Call BindJSON to bind the received JSON to\n        // newAlbum.\n        if err := c.BindJSON(\u0026newAlbum); err != nil {\n                return\n        }\n\n        // Add the new album to the slice.\n        albums = append(albums, newAlbum)\n        c.IndentedJSON(http.StatusCreated, newAlbum)\n}\n```\nIn this code, you:\n\n- Use Context.BindJSON to bind the request body to newAlbum.\n- Append the album struct initialized from the JSON to the albums slice.\n- Add a 201 status code to the response, along with JSON representing the album you added.\n\n2. Change your main function so that it includes the router.POST function, as in the following.\n```Go\nfunc main() {\n        router := gin.Default()\n        router.GET(\"/albums\", getAlbums)\n        router.POST(\"/albums\", postAlbums)\n\n        router.Run(\"localhost:8080\")\n}\n```\nIn this code, you:\n\n- Associate the POST method at the /albums path with the postAlbums function.\n\nWith Gin, you can associate a handler with an HTTP method-and-path combination. In this way, you can separately route requests sent to a single path based on the method the client is using.\n\n3. Save main.go.\n\n### Run the code\n1. In the first Cloud Shell terminal window, if the server is still running from the last section, stop it.\n\n2. In the first Cloud Shell terminal window, run the code.\n```sh\ngo run .\n```\n3. In the other Cloud Shell terminal window, use the following curl command to make a request to your running web service.\n```sh\ncurl http://localhost:8080/albums --include --header    \"Content-Type: application/json\" --request \"POST\" --data '{\"id\": \"4\",\"title\": \"The Modern Sound of Betty Carter\",\"artist\": \"Betty Carter\",\"price\": 49.99}'\n```\nThe command should display headers and JSON for the added album.\n```sh\nHTTP/1.1 201 Created\nContent-Type: application/json; charset=utf-8\nDate: Wed, 02 Jun 2021 00:34:12 GMT\nContent-Length: 116\n\n{\n    \"id\": \"4\",\n    \"title\": \"The Modern Sound of Betty Carter\",\n    \"artist\": \"Betty Carter\",\n    \"price\": 49.99\n}\n```\n4. As in the previous section, use curl to retrieve the full list of albums, which you can use to confirm that the new album was added.\n```sh\ncurl http://localhost:3000/albums --header \"Content-Type: application/json\" --request \"GET\"\n```\nThe command should display the album list.\n```json\n[\n        {\n                \"id\": \"1\",\n                \"title\": \"Blue Train\",\n                \"artist\": \"John Coltrane\",\n                \"price\": 56.99\n        },\n        {\n                \"id\": \"2\",\n                \"title\": \"Jeru\",\n                \"artist\": \"Gerry Mulligan\",\n                \"price\": 17.99\n        },\n        {\n                \"id\": \"3\",\n                \"title\": \"Sarah Vaughan and Clifford Brown\",\n                \"artist\": \"Sarah Vaughan\",\n                \"price\": 39.99\n        },\n        {\n                \"id\": \"4\",\n                \"title\": \"The Modern Sound of Betty Carter\",\n                \"artist\": \"Betty Carter\",\n                \"price\": 49.99\n        }\n]\n```\n\nIn the next section, you'll add code to handle a GET for a specific item.\n\n\n# Step 5\n## Step 5 of 7\n### Write a handler to return a specific item\nWhen the client makes a request to GET /albums/[id], you want to return the album whose ID matches the id path parameter.\n\nTo do this, you will:\n\n- Add logic to retrieve the requested album.\n- Map the path to the logic.\n\n### Write the code\n1. Beneath the postAlbums function you added in the preceding section, paste the following code to retrieve a specific album.\n\nThis getAlbumByID function will extract the ID in the request path, then locate an album that matches.\n```go\n// getAlbumByID locates the album whose ID value matches the id\n// parameter sent by the client, then returns that album as a response.\nfunc getAlbumByID(c *gin.Context) {\n        id := c.Param(\"id\")\n\n        // Loop over the list of albums, looking for\n        // an album whose ID value matches the parameter.\n        for _, a := range albums {\n                if a.ID == id {\n                        c.IndentedJSON(http.StatusOK, a)\n                        return\n                }\n        }\n        c.IndentedJSON(http.StatusNotFound, gin.H{\"message\": \"album not found\"})\n}\n```\nIn this code, you:\n\n- Use Context.Param to retrieve the id path parameter from the URL. When you map this handler to a path, you'll include a placeholder for the parameter in the path.\n\n- Loop over the album structs in the slice, looking for one whose ID field value matches the id parameter value. If it's found, you serialize that album struct to JSON and return it as a response with a 200 OK HTTP code.\n\nAs mentioned above, a real-world service would likely use a database query to perform this lookup.\n\n- Return an HTTP 404 error with http.StatusNotFound if the album isn't found.\n\n2. Finally, change your main so that it includes a new call to router.GET, where the path is now /albums/:id, as shown in the following example.\n```go\nfunc main() {\n        router := gin.Default()\n        router.GET(\"/albums\", getAlbums)\n        router.GET(\"/albums/:id\", getAlbumByID)\n        router.POST(\"/albums\", postAlbums)\n\n        router.Run(\"localhost:8080\")\n}\n```\nIn this code, you:\n\n- Associate the /albums/:id path with the getAlbumByID function. In Gin, the colon preceding an item in the path signifies that the item is a path parameter.\n\n### Run the code\n1. In the first Cloud Shell terminal window, if the server is still running from the last section, stop it.\n\n2. In the first Cloud Shell terminal window, run the code.\n```sh\ngo run .\n```\n\n3. In the other Cloud Shell terminal window, use curl to make a request to your running web service.\n```sh\ncurl http://localhost:8080/albums/2\n```\nThe command should display JSON for the album whose ID you used. If the album wasn't found, you'll get JSON with an error message.\n```json\n{\n        \"id\": \"2\",\n        \"title\": \"Jeru\",\n        \"artist\": \"Gerry Mulligan\",\n        \"price\": 17.99\n}\n```\nContinue to the last section for links to useful content.\n\n# Step 6 \n## Step 6 of 7\n### Conclusion\nCongratulations! You've just used Go and Gin to write a simple RESTful web service.\n\nSuggested next topics:\n\n- If you're new to Go, you'll find useful best practices described in Effective Go and How to write Go code.\n- The Go Tour is a great step-by-step introduction to Go fundamentals.\n- For more about Gin, see the Gin Web Framework package documentation or the Gin Web Framework docs.\n\nContinue to the next section to view the full code for the application you build with this tutorial.\n\n# Step 7\n## Step 7 of 7\n### Completed code\nThis section contains the code for the application you build with this tutorial.\n```go\npackage main\n\nimport (\n        \"net/http\"\n\n        \"github.com/gin-gonic/gin\"\n)\n\n// album represents data about a record album.\ntype album struct {\n        ID     string  `json:\"id\"`\n        Title  string  `json:\"title\"`\n        Artist string  `json:\"artist\"`\n        Price  float64 `json:\"price\"`\n}\n\n// albums slice to seed record album data.\nvar albums = []album{\n        {ID: \"1\", Title: \"Blue Train\", Artist: \"John Coltrane\", Price: 56.99},\n        {ID: \"2\", Title: \"Jeru\", Artist: \"Gerry Mulligan\", Price: 17.99},\n        {ID: \"3\", Title: \"Sarah Vaughan and Clifford Brown\", Artist: \"Sarah Vaughan\", Price: 39.99},\n}\n\nfunc main() {\n        router := gin.Default()\n        router.GET(\"/albums\", getAlbums)\n        router.GET(\"/albums/:id\", getAlbumByID)\n        router.POST(\"/albums\", postAlbums)\n\n        router.Run(\"localhost:8080\")\n}\n\n// getAlbums responds with the list of all albums as JSON.\nfunc getAlbums(c *gin.Context) {\n        c.IndentedJSON(http.StatusOK, albums)\n}\n\n// postAlbums adds an album from JSON received in the request body.\nfunc postAlbums(c *gin.Context) {\n        var newAlbum album\n\n        // Call BindJSON to bind the received JSON to\n        // newAlbum.\n        if err := c.BindJSON(\u0026newAlbum); err != nil {\n                return\n        }\n\n\n        // Add the new album to the slice.\n        albums = append(albums, newAlbum)\n        c.IndentedJSON(http.StatusCreated, newAlbum)\n}\n\n// getAlbumByID locates the album whose ID value matches the id\n// parameter sent by the client, then returns that album as a response.\nfunc getAlbumByID(c *gin.Context) {\n        id := c.Param(\"id\")\n\n        // Loop through the list of albums, looking for\n        // an album whose ID value matches the parameter.\n        for _, a := range albums {\n                if a.ID == id {\n                        c.IndentedJSON(http.StatusOK, a)\n                        return\n                }\n        }\n        c.IndentedJSON(http.StatusNotFound, gin.H{\"message\": \"album not found\"})\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenstr%2Fweb-service-gin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevenstr%2Fweb-service-gin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenstr%2Fweb-service-gin/lists"}