{"id":49585043,"url":"https://github.com/robertwhurst/zephyr","last_synced_at":"2026-05-03T22:05:17.335Z","repository":{"id":291080130,"uuid":"741309418","full_name":"RobertWHurst/Zephyr","owner":"RobertWHurst","description":"Zephyr is a REST microservice framework. It contains gateway logic for streams and http as well as service registration and communication","archived":false,"fork":false,"pushed_at":"2026-03-28T17:32:30.000Z","size":23013,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2026-03-28T19:23:59.747Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://telemetrytv.com","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RobertWHurst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-01-10T06:01:06.000Z","updated_at":"2026-03-28T17:32:32.000Z","dependencies_parsed_at":"2025-05-20T17:31:36.807Z","dependency_job_id":"529ed7e9-ed23-4136-b5d9-12031fd21316","html_url":"https://github.com/RobertWHurst/Zephyr","commit_stats":null,"previous_names":["telemetrytv/zephyr","robertwhurst/zephyr"],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/RobertWHurst/Zephyr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobertWHurst%2FZephyr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobertWHurst%2FZephyr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobertWHurst%2FZephyr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobertWHurst%2FZephyr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobertWHurst","download_url":"https://codeload.github.com/RobertWHurst/Zephyr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobertWHurst%2FZephyr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32586189,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":"2026-05-03T22:05:16.409Z","updated_at":"2026-05-03T22:05:17.329Z","avatar_url":"https://github.com/RobertWHurst.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zephyr\n\n\u003cp\u003e\n  \u003cimg src=\"zephyr.png\" align=\"right\" width=\"400\"\u003e\n\u003c/p\u003e\n\nZephyr is a microservice framework developed by the TelemetryTV team. It brings\nthe HTTP protocol right to your services without compromise. This means that\nevery service you write feels like it's a simple web right at the edge, rather\nthat behind a complex messaging system.\n\nThis illusion is achieved by allowing services to declare their public routes to\na gateway over a transport method such as NATS.\n\nZephyr provides three important components:\n\n- **Gateway**: Indexes services it finds via the assigned transport, and routes\n  HTTP requests to them. Implements go's http.Handler, so it can be used with\n  any http server.\n- **Service**: Provides a mechanism for services to declare their public routes,\n  as well as receive requests via a gateway.\n- **Client**: Used to make requests from service to service. Clients are special\n  in that they provide a http client like API, and can make requests to routes\n  that are not public, and are not routable from a gateway.\n\nAll of these components will work provided that the transport associated with\nthem use a shared network. For example, if using a NATS transport, as long as\nall components are connected to the same NATS cluster, they will be able to\ndiscover and communicate with each other.\n\n\u003cbr clear=\"right\" /\u003e\n\n\u003e **A Note on Navaros:**\n\u003e\n\u003e Navaros is a router package that Zephyr has been designed to work with. It is\n\u003e not required to use Zephyr, as any http.Handler can be used with the service\n\u003e and gateway components. However, Navaros has the concept of public and private\n\u003e routes built in, thus using it means you do not need to declare you service\n\u003e routes manually.\n\u003e\n\u003e See the [Navaros](https://github.com/RobertWHurst/navaros) repository for more\n\u003e information.\n\n## Working with Zephyr\n\nUsing Zephyr for your microservice architecture is simple.\n\nYou will need at least two services to start. One service we will be a gateway.\nThe other will be our first api service. The gateway will be the entry point for\nall HTTP requests originating from outside the service network. The api service\nwill register routes with the gateway, and handle requests forwarded to it by\nthe gateway.\n\n### Creating a Service\n\nWe do this using the `zephyr.NewService` function, which takes three arguments:\n\n- The name of the service\n- A transport to use for communication\n- The handler to use for incoming requests\n\n```go\nservice := zephyr.NewService(\"myservice\", natstransport.New(natsConn), Handler)\nservice.Run()\n```\n\nA more complete illustration can be found in the example below.\n\n\u003cdetails\u003e\n\u003csummary\u003eService Example\u003c/summary\u003e\n\nThis service is a sketch of a simple CRUD service. The aim here is to show you\nhow Zephyr can be used with any http library, in this case Navaros.\n\n```go\npackage main\n\nimport (\n  \"fmt\"\n  \"net/http\"\n\n  \"github.com/nats-io/nats.go\"\n  \"github.com/RobertWHurst/zephyr\"\n  natstransport \"github.com/RobertWHurst/zephyr/nats-transport\"\n  \"github.com/RobertWHurst/navaros\"\n  \"github.com/RobertWHurst/navaros/middleware/json\"\n)\n\nconst natsURL = \"nats://localhost:4222\"\n\nvar Router = navaros.NewRouter()\n\nfunc init() {\n  Router.Use(json.Middleware(nil))\n\n  Router.PublicCreate(\"/item\", createItem)\n  Router.PublicGet(\"/item\", getItems)\n  Router.PublicGet(\"/item/:id\", getItem)\n  Router.PublicUpdate(\"/item/:id\", updateItem)\n  Router.PublicDelete(\"/item/:id\", deleteItem)\n}\n\nfunc main() {\n\n  // Connect to the NATS\n  conn, err := nats.Connect(natsURL)\n  if err != nil {\n    fmt.Printf(\"Failed to connect to NATS: %s\\n\", err)\n    return\n  }\n\n  // Create a new service\n  service := zephyr.NewService(\"myservice\", natstransport.New(conn), Router)\n\n  // Run the service\n  if err := service.Run(); err != nil {\n    fmt.Printf(\"Failed to run service: %s\\n\", err)\n  }\n}\n\ntype Item struct {\n  ID int `json:\"id\"`\n  Name string `json:\"name\"`\n  Description string `json:\"description\"`\n}\n\nidIndex := 1\nvar items []*Item\n\nfunc createItem(ctx *navaros.Context) {\n  item := \u0026Item{}\n  if err := ctx.UnmarshalRequestBody(item); err != nil {\n    ctx.Status = http.StatusBadRequest\n    return\n  }\n  \n  var fieldErrors []*json.FieldError\n  if item.Name == \"\" {\n    fieldErrors = append(fieldErrors, \u0026json.FieldError{\n      Field: \"name\",\n      Message: \"Name is required\",\n    })\n  }\n  if item.Description == \"\" {\n    fieldErrors = append(fieldErrors, \u0026json.FieldError{\n      Field: \"description\",\n      Message: \"Description is required\",\n    })\n  }\n  if len(fieldErrors) != 0 {\n    ctx.Body = fieldErrors\n    return\n  }\n\n  item.ID = idIndex += 1\n  items = append(items, item)\n\n  ctx.Status = http.StatusCreated\n}\n\nfunc getItems(ctx *navaros.Context) {\n  ctx.Body = items\n}\n\nfunc getItem(ctx *navaros.Context) {\n  idStr := ctx.Params().Get(\"id\")\n  id, err := strconv.Atoi(idStr)\n  if err != nil {\n    ctx.Status = http.StatusBadRequest\n    return\n  }\n\n  var targetItem *Item\n  for _, item := range items {\n    if item.ID == id {\n      targetItem = item\n      break\n    }\n  }\n\n  if targetItem == nil {\n    return\n  }\n\n  ctx.Body = targetItem\n}\n\nfunc updateItem(ctx *navaros.Context) {\n  idStr := ctx.Params().Get(\"id\")\n  id, err := strconv.Atoi(idStr)\n  if err != nil {\n    ctx.Status = http.StatusBadRequest\n    return\n  }\n\n  var targetItem *Item\n  for _, item := range items {\n    if item.ID == id {\n      targetItem = item\n      break\n    }\n  }\n\n  if targetItem == nil {\n    return\n  }\n\n  item := \u0026Item{}\n  if err := ctx.UnmarshalRequestBody(item); err != nil {\n    ctx.Status = http.StatusBadRequest\n    return\n  }\n\n  if item.Name != \"\" {\n    targetItem.Name = item.Name\n  }\n  if item.Description != \"\" {\n    targetItem.Description = item.Description\n  }\n\n  ctx.Status = http.NoContent\n}\n\n```\n\n\u003c/details\u003e\n\n### Creating a Gateway\n\nIn order for our service to be accessible, we will need a way to get requests\nfrom the outside world to our service. This is where the gateway comes in.\n\nA gateway is a service that can listen for HTTP requests from outside your\nservice network. It is responsible for routing these requests to the correct\nservice. Zephyr provides a gateway struct which handles this responsibility.\n\nTo create a gateway, we use the `zephyr.NewGateway` function, which takes two\narguments:\n\n- The name of the gateway.\n- A transport to use for communication.\n\nThe returned gateway can then be used as an http.Handler with go's http package.\n\n```go\ngateway := zephyr.NewGateway(\"mygateway\", natstransport.New(natsConn))\nhttp.ListenAndServe(\":8080\", gateway)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eGateway Example\u003c/summary\u003e\n\nThis gateway is a simple example of a gateway that listens for HTTP requests on\nport 8080, and forwards them to the service we created in the previous example.\n\n```go\npackage main\n\nimport (\n  \"fmt\"\n  \"net/http\"\n\n  \"github.com/nats-io/nats.go\"\n  \"github.com/RobertWHurst/zephyr\"\n  natstransport \"github.com/RobertWHurst/zephyr/nats-transport\"\n)\n\nconst natsURL = \"nats://localhost:4222\"\n\nfunc main() {\n  // Connect to the NATS\n  conn, err := nats.Connect(natsURL)\n  if err != nil {\n    fmt.Printf(\"Failed to connect to NATS: %s\\n\", err)\n    return\n  }\n\n  // Create a new gateway\n  gateway := zephyr.NewGateway(\"mygateway\", natstransport.New(conn))\n\n  // Listen for requests\n  if err := http.ListenAndServe(\":8080\", gateway); err != nil {\n    fmt.Printf(\"Failed to listen and serve: %s\\n\", err)\n  }\n}\n```\n\n\u003c/details\u003e\n\n### More than One Service\n\nIf we only wanted to run a single service, we wouldn't need Zephyr in the first\nplace. At some point, we will want to add more services. These additional\nservices follow the same pattern outlined earlier. The gateway will automatically\ndiscover these services and route requests to them.\n\n### Making Service to Service Requests\n\nAt some point a service will need to make a request to another service. This is\nwhere the zephyr client comes in. It provides a http client like API and allows\nyou to call non-public routes.\n\n```go\nclient := zephyr.NewClient(natstransport.New(natsConn))\nresp, err := client.Service(\"myservice\").Get(\"/item/1\")\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eClient Request Examples\u003c/summary\u003e\n\nHere are some examples of how you can use the client to make requests to other\nservices.\n\n```go\npackage main\n\nimport (\n  \"bytes\"\n  \"fmt\"\n  \"net/http\"\n\n  \"github.com/nats-io/nats.go\"\n  \"github.com/RobertWHurst/zephyr\"\n  natstransport \"github.com/RobertWHurst/zephyr/nats-transport\"\n)\n\nconst natsURL = \"nats://localhost:4222\"\n\ntype Item struct {\n  ID int `json:\"id\"`\n  Name string `json:\"name\"`\n  Description string `json:\"description\"`\n}\n\nfunc main() {\n  // Connect to the NATS\n  conn, err := nats.Connect(natsURL)\n  if err != nil {\n    fmt.Printf(\"Failed to connect to NATS: %s\\n\", err)\n    return\n  }\n\n  // Create a new client\n  client := zephyr.NewClient(natstransport.New(conn))\n\n  // Make a request to the myservice service\n  resp1, err := client.Service(\"myservice\").Get(\"/item/1\")\n  if err != nil {\n    fmt.Printf(\"Failed to make request: %s\\n\", err)\n    return\n  }\n  if resp1.StatusCode != http.StatusOK {\n    fmt.Printf(\"Failed to get item: %d\\n\", resp1.StatusCode)\n    return\n  }\n  fmt.Printf(\"Response 1: %v\\n\", resp1)\n\n  newItem := \u0026Item{\n    Name: \"New Item\",\n    Description: \"A new item\",\n  }\n  resp2, err := client.Service(\"myservice\").Post(\"/item\", newItem)\n  if err != nil {\n    fmt.Printf(\"Failed to make request: %s\\n\", err)\n    return\n  }\n  if resp2.StatusCode != http.StatusCreated {\n    fmt.Printf(\"Failed to create item: %d\\n\", resp2.StatusCode)\n    return\n  }\n  fmt.Printf(\"Response 2: %v\\n\", resp2)\n\n  updatedItem := \u0026Item{\n    Name: \"Updated Item\",\n  }\n  updatedItemBuf, err := json.Marshal(updatedItem)\n  if err != nil {\n    fmt.Printf(\"Failed to marshal item: %s\\n\", err)\n    return\n  }\n  req3 := http.NewRequest(http.MethodPut, \"http://myservice/item/1\", bytes.NewReader(updatedItemBuf))\n  resp3, err := client.Do(req3)\n  if err != nil {\n    fmt.Printf(\"Failed to make request: %s\\n\", err)\n    return\n  }\n  if resp3.StatusCode != http.StatusNoContent {\n    fmt.Printf(\"Failed to update item: %d\\n\", resp3.StatusCode)\n    return\n  }\n  fmt.Printf(\"Response 3: %v\\n\", resp3)\n}\n```\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertwhurst%2Fzephyr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertwhurst%2Fzephyr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertwhurst%2Fzephyr/lists"}