{"id":18230425,"url":"https://github.com/benc-uk/go-rest-api","last_synced_at":"2025-04-03T15:30:22.681Z","repository":{"id":57494564,"uuid":"194839753","full_name":"benc-uk/go-rest-api","owner":"benc-uk","description":"Boilerplate \u0026 template starter for creating a REST based HTTP microservice in Go","archived":false,"fork":false,"pushed_at":"2023-11-27T23:49:42.000Z","size":24680,"stargazers_count":10,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2023-11-28T00:32:37.406Z","etag":null,"topics":["containers","go","golang","microservices","rest-api"],"latest_commit_sha":null,"homepage":"","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/benc-uk.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}},"created_at":"2019-07-02T10:12:41.000Z","updated_at":"2023-12-20T15:19:12.713Z","dependencies_parsed_at":"2023-11-11T17:21:53.210Z","dependency_job_id":"e5c73ef6-1a6f-4ad4-b605-9d82864c511d","html_url":"https://github.com/benc-uk/go-rest-api","commit_stats":{"total_commits":20,"total_committers":1,"mean_commits":20.0,"dds":0.0,"last_synced_commit":"03b8f2c18edf6dded51fa9be747ac4e92e142eb5"},"previous_names":["benc-uk/go-starter"],"tags_count":12,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benc-uk%2Fgo-rest-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benc-uk%2Fgo-rest-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benc-uk%2Fgo-rest-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benc-uk%2Fgo-rest-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benc-uk","download_url":"https://codeload.github.com/benc-uk/go-rest-api/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247027630,"owners_count":20871566,"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","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":["containers","go","golang","microservices","rest-api"],"created_at":"2024-11-04T11:04:09.109Z","updated_at":"2025-04-03T15:30:22.360Z","avatar_url":"https://github.com/benc-uk.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go - REST API Starter Kit \u0026 Library\n\nThis is a set of packages for creating REST \u0026 HTTP based microservices / backend servers in Go, with supporting functions and helpers. It's fairly opinionated and acts a little like a very minimal mini framework.\n\nIt purposefully doesn't use any web framework instead focusing on the base HTTP library and [Chi](https://github.com/go-chi/chi) for routing. Approaches such as composition which are idiomatic to Go, rather than classic dependency injection have been used.\n\nThe `cmd` folder has an example of a working server/service which will accept REST requests and has a minimal API, serving as a reference.\n\nThe `pkg` folder has a number of Go packages to support running REST APIs in Go, these are described in detail below\n\n```\npkg/\n├── api\n├── auth\n├── dapr/pubsub\n├── env\n├── httptester\n├── problem\n├── sse\n└── static\n```\n\nThis has been developed in Go 1.19+ and follows the https://github.com/golang-standards/project-layout guidelines for project structure. Which might be an acquired taste.\n\nAlso included are a standard and reusable Dockerfile \u0026 makefile, both of which inject version information into the build. The Makefile will also handle linting and running with hot-reload via `air`\n\n## Package `api`\n\nThis is a baseline from which you can extend, in order to run your own API, see the `cmd/server.go` for an example of how this is done. A quick summary is:\n\n```go\nimport \"github.com/benc-uk/go-rest-api/pkg/api\"\n\ntype MyAPI struct {\n  // Embed and wrap the base API struct\n  *api.Base\n  \n  // Add extra fields as per your implementation\n  foo Foo\n}\n\nrouter := chi.NewRouter()\napi := MyAPI{\n  api.NewBase(serviceName, version, buildInfo, healthy),\n}\n\napi.AddHealthEndpoint(router, \"health\")\napi.AddStatusEndpoint(router, \"status\")\n```\n\nThe base API supports health checks and exposes data such as version and service name, plus helper functions for sending JSON \u0026 plain text responses or errors.\n\nOptional endpoints which can be added to the API:\n\n- Status endpoint, returning server \u0026 service details as JSON\n- Prometheus metrics\n- Health check\n- Any routes you wish to return \"200 OK\" such as the root (/)\n\nOptional middleware can be configured:\n\n- Enabling permissive CORS policy (suggest you use chi/cors for finer grained control)\n- Enriching HTTP request context with data extracted from JWT token. If a JWT token is found on any request, you can specify a claim, and the value of that claim will be extracted and put into the HTTP request context.\n\nSupporting functions of the base API struct are, providing common API use cases:\n\n```go\nStartServer(port int, router chi.Router, timeout time.Duration)\nReturnJSON(w http.ResponseWriter, data interface{})\nReturnText(w http.ResponseWriter, msg string)\nReturnErrorJSON(w http.ResponseWriter, err error)\nReturnOKJSON(w http.ResponseWriter)\n```\n\n## Package `auth`\n\nThis package contains `Validator` interface which can be configured and used to enforce authentication on some or all routes of the API.\n\n📝 Note: This package is generic and can be used with any code utilizing the `net/http` library\n\nThere are two implementations of the `Validator` interface:\n\n- `PassthroughValidator` - Used when mocking \u0026 testing, or to conditionally switch auth off\n- `JWTValidator` - Main JWT based validator\n\nThe `JWTValidator` takes three parameters when created:\n\n- *Client ID*: An application client ID used when validating tokens, by checking the `aud` claim.\n- *Scope*: A scope string, validated against the `scp` claim.\n- *JWKS URL*: A URL of the keystore used to fetch public keys and and verify the signature of the token. This assumes tokens are signed with a public/private key algorithm e.g. RSA\n\nIt can be used two ways: `router.Use(jwtValidator.Middleware)` to add validating middleware to all routes on a router. Alternatively `jwtValidator.Protect(myHandler)` to wrap and protect certain handlers\n\nFailed validation results in a HTTP 401 being returned.\n\n## Package `env`\n\nVery basic set of helpers for fetching env vars with fallbacks to default values.\n\n## Package `problem`\n\nProvides support for RFC-7807 standard formatted responses to API errors. Use the `Wrap()` function to wrap an error, and then `Send()` to write it to the HTTP response writer.\n\n```go\n// A rather contrived example\nfunc (api MyAPI) getThing(resp http.ResponseWriter, req *http.Request) {\n  id := \"some_id\"\n  thing, err := api.dbContext.ExecuteSomeQuery(id, blah, blah)\n\n  // Return a RFC-7807 problem wrapping the database error, HTTP 500 will be sent\n  if err != nil {\n    problem.Wrap(500, req.RequestURI, \"thing:\"+id, err).Send(resp)\n    return\n  }\n\n  // Return a RFC-7807 problem describing the missing thing, HTTP 404 will be sent\n  if thing == nil {\n    problem.Wrap(404, req.RequestURI, \"thing:\"+id, errors.New(\"thing with that ID does not exist\")).Send(resp)\n    return\n  }\n\n  // Handle success\n}\n```\n\n## Package `static`\n\nIncludes a `SpaHandler` for serving SPA style static applications which may contain client routing logic. It acts as a wrapper around the standard `http.FileServer` but rather than returning 404s it will return a fallback index file, e.g. *index.html*\n\nUsage:\n\n```go\nr := chi.NewRouter()\n\nr.Handle(\"/*\", static.SpaHandler{\n  StaticPath: \"./\",         // Path to app content directory\n  IndexFile:  \"index.html\", // Name of your SPA HTML file\n})\n\nsrv := \u0026http.Server{\n  Handler:      r,\n  Addr:         \":8080\",\n}\n\nlog.Fatal(srv.ListenAndServe())\n```\n\n## Package `httptester`\n\nUsed to run through multiple test cases when integration testing an API or any HTTP service. Use the `httptester.TestCase` struct and pass an array of them to `httptester.Run()`\n\n## Package `dapr/pubsub`\n\nUse to register your API with Dapr pub-sub and subscribe to a given topic and register a callback handler for messages received at that topic.\n\n## Package `logging`\n\nProvides `FilteredRequestLogger` an extension of chi middleware logger which supports filtering out of requests from the logging output.\n\n## Package `sse`\n\nProvides support for [Server Side Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events). Two implementations are provided:\n\n- Simple backend helper that can stream SSE events over HTTP\n- A message broker which can be used to send messages to multiple clients, groups and keep track of connections/disconnections\n\nNote. This package is standalone and will work with any Go HTTP implementation, you don't need to be using the `api` or the other packages here.\n\nStream usage:\n\n```go\nsrv := sse.NewStreamer[string]()\n\n// Send the time to the user every 1 second\ngo func() {\n  for {\n    timeNow := time.Now().Format(\"15:04:05\")\n    srv.Messages \u003c- \"Hello it is now \" + timeNow\n    time.Sleep(1 * time.Second)\n  }\n}()\n\nhttp.HandleFunc(\"/stream-time\", func(w http.ResponseWriter, r *http.Request) {\n  srv.Stream(w, *r)\n})\n```\n\nBroker usage:\n\n```go\nsrv := sse.NewBroker[string]()\n\n// MessageAdapter is optional, but can format messages\nsrv.MessageAdapter = func(message string, clientID string) sse.SSE {\n  return sse.SSE{\n    Event: \"message\",\n    Data:  \"Some prefix \" + message,\n  }\n}\n\n// Send a message every 1 second\ngo func() {\n  for {\n    srv.SendToAll(\"Hello! \" + time.Now().String())\n    time.Sleep(1 * time.Second)\n  }\n}()\n\nhttp.HandleFunc(\"/stream-events\", func(w http.ResponseWriter, r *http.Request) {\n  clientID := \"You need to implement something here\"\n  srv.Stream(clientID, w, *r)\n})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenc-uk%2Fgo-rest-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenc-uk%2Fgo-rest-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenc-uk%2Fgo-rest-api/lists"}