{"id":18742160,"url":"https://github.com/donuts-are-good/puggy","last_synced_at":"2025-11-20T22:30:16.004Z","repository":{"id":143197776,"uuid":"613675066","full_name":"donuts-are-good/puggy","owner":"donuts-are-good","description":"http request router","archived":false,"fork":false,"pushed_at":"2023-04-06T17:14:37.000Z","size":10,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-14T20:56:36.508Z","etag":null,"topics":["go","http","router"],"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/donuts-are-good.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}},"created_at":"2023-03-14T03:24:11.000Z","updated_at":"2023-04-07T08:05:23.000Z","dependencies_parsed_at":"2023-05-23T18:30:17.103Z","dependency_job_id":null,"html_url":"https://github.com/donuts-are-good/puggy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donuts-are-good%2Fpuggy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donuts-are-good%2Fpuggy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donuts-are-good%2Fpuggy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/donuts-are-good%2Fpuggy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/donuts-are-good","download_url":"https://codeload.github.com/donuts-are-good/puggy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239620933,"owners_count":19669791,"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":["go","http","router"],"created_at":"2024-11-07T16:06:46.156Z","updated_at":"2025-11-20T22:30:15.930Z","avatar_url":"https://github.com/donuts-are-good.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dont use this yet, it's broken.\n\n![donuts-are-good's followers](https://img.shields.io/github/followers/donuts-are-good?\u0026color=555\u0026style=for-the-badge\u0026label=followers) ![donuts-are-good's stars](https://img.shields.io/github/stars/donuts-are-good?affiliations=OWNER%2CCOLLABORATOR\u0026color=555\u0026style=for-the-badge) ![donuts-are-good's visitors](https://komarev.com/ghpvc/?username=donuts-are-good\u0026color=555555\u0026style=for-the-badge\u0026label=visitors)\n\n# **🐾 Puggy**  \nIntroducing Puggy, a simple webapp router w/ CORS!\n\n## **🦴 What Makes Puggy Special?**\n-\tLightweight! \n-   No dependencies!\n-   Supports CORS out of the box!\n-   URL path variables support `/articles/{article-slug}`\n\n## **🤔 Why Was Puggy Made?**\n\nI really liked [gorilla/mux](https://github.com/gorilla/mux) but it's now archived and though I considered stepping up to maintain it, it wouldn't be fair to maintain only the parts I know and used the most, so I made a new thing that accomplished all the things I loved [gorilla/mux](https://github.com/gorilla/mux) for, and hopefully will help other people as well.\n\n## **🎉 Getting Started**\n\nFirst, `go get github.com/donuts-are-good/puggy` and import it at the top of your project:\n\n```\nimport \"github.com/donuts-are-good/puggy\"\n```\n\nNext, create a new router by calling `NewRouter` with an array of CORS domains:\n\n```\ncorsDomains := []string{\n    \"puggy.local\", \n    \"pugtastic.com\",\n    }\n\nr := puggy.NewRouter(corsDomains)\n```\n\nThen, add your routes using the `AddRoute` method:\n\n\n```\nr.AddRoute(\"GET\", \"/\", handlerRoot) \nr.AddRoute(\"POST\", \"/users\", handlerUsers)\n```\n\nFinally, pass your router instance to the `http.ListenAndServe` method to start your server:\n\n```\npugPort := \":8080\"\nhttp.ListenAndServe(pugPort, r)\n```\n\nAnd that's it! You now have a working webapp using Puggy as your router.\n\n## **💡 Usage**\n\nHere are some examples to get you started with Puggy:\n\n### **Simple route and handler example**\n\n```\n// handler\nfunc handlerRoot(w http.ResponseWriter, req *http.Request) { \t\n    w.Write([]byte(\"Hello, World!\")) \n}\n\n// route\nr.AddRoute(\"GET\", \"/\", handlerRoot)\n```\n\n### **Route with path variable example**\n\n```\n// handler\nfunc handlerUsers(w http.ResponseWriter, req *http.Request) {\n    userID := req.Context().Value(\"userID\").(string) \t\n    w.Write([]byte(\"Hello, User \" + userID)) \n}\n\n// route\nr.AddRoute(\"GET\", \"/users/{userID}\", handlerUsers)\n```\n\n### **POST request with JSON payload example**\n\n\n```\n// handler\nfunc handlerUserJSON(w http.ResponseWriter, req *http.Request) { \t\n    var user struct { \t\t\n        Name string `json:\"name\"` \t\t\n        Age  int    `json:\"age\"` \t\n    }  \t\n    if err := json.NewDecoder(req.Body).Decode(\u0026user); err != nil {\n        http.Error(w, err.Error(), http.StatusBadRequest) \t\t\n        return\n    }  \t\n    w.Write([]byte(\"Hello, \" + user.Name)) \n}\n\n// route\nr.AddRoute(\"POST\", \"/users\", handlerUserJSON)\n```\n\n\n\n## **🚀 Full CRUD example**\n```\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n    \"github.com/donuts-are-good/puggy\"\n)\n\nvar books = []struct {\n\tID     string `json:\"id\"`\n\tTitle  string `json:\"title\"`\n\tAuthor string `json:\"author\"`\n}{\n\t{ID: \"1\", Title: \"The Great Gatsby\", Author: \"F. Scott Fitzgerald\"},\n\t{ID: \"2\", Title: \"To Kill a Mockingbird\", Author: \"Harper Lee\"},\n\t{ID: \"3\", Title: \"Pride and Prejudice\", Author: \"Jane Austen\"},\n}\n\nfunc main() {\n\tr := puggy.NewRouter([]string{\n        \"puggy.local\", \n        \"pugtaculous.local\",\n        })\n\t\n\t// Get all books\n\tr.AddRoute(\"GET\", \"/books\", getBooks)\n\t\n\t// Get a single book by ID\n\tr.AddRoute(\"GET\", \"/books/{bookID}\", getBook)\n\t\n\t// Create a new book\n\tr.AddRoute(\"POST\", \"/books\", createBook)\n\t\n\t// Update a book by ID\n\tr.AddRoute(\"PUT\", \"/books/{bookID}\", updateBook)\n\t\n\t// Delete a book by ID\n\tr.AddRoute(\"DELETE\", \"/books/{bookID}\", deleteBook)\n\t\n\thttp.ListenAndServe(\":8000\", r)\n}\n\nfunc getBooks(w http.ResponseWriter, req *http.Request) {\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tjson.NewEncoder(w).Encode(books)\n}\n\nfunc getBook(w http.ResponseWriter, req *http.Request) {\n\tbookID := req.Context().Value(\"bookID\").(string)\n\tfor _, book := range books {\n\t\tif book.ID == bookID {\n\t\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t\tjson.NewEncoder(w).Encode(book)\n\t\t\treturn\n\t\t}\n\t}\n\thttp.NotFound(w, req)\n}\n\nfunc createBook(w http.ResponseWriter, req *http.Request) {\n\tvar book struct {\n\t\tID     string `json:\"id\"`\n\t\tTitle  string `json:\"title\"`\n\t\tAuthor string `json:\"author\"`\n\t}\n\tif err := json.NewDecoder(req.Body).Decode(\u0026book); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\tbooks = append(books, book)\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusCreated)\n\tjson.NewEncoder(w).Encode(book)\n}\n\nfunc updateBook(w http.ResponseWriter, req *http.Request) {\n\tbookID := req.Context().Value(\"bookID\").(string)\n\tvar newBook struct {\n\t\tID     string `json:\"id\"`\n\t\tTitle  string `json:\"title\"`\n\t\tAuthor string `json:\"author\"`\n\t}\n\tif err := json.NewDecoder(req.Body).Decode(\u0026newBook); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\tfor i, book := range books {\n\t\tif book.ID == bookID {\n\t\t\tbooks[i] = newBook\n\t\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\t\tjson.NewEncoder(w).Encode(newBook)\n\t\t\treturn\n\t\t}\n\t}\n\thttp.NotFound(w, req)\n}\n\nfunc deleteBook(w http.ResponseWriter, req *http.Request) {\n\tbookID := req.Context().Value(\"bookID\").(string)\n\tfor i, book := range books {\n\t\tif book.ID == bookID {\n\t\t\tbooks = append(books[:i], books[i+1:]...)\n\t\t\tw.WriteHeader(http.StatusNoContent)\n\t\t\treturn\n\t\t}\n\t}\n\thttp.NotFound(w, req)\n}\n\n\n```\n\n\n\n## Greetz\n\nthe Dozens, code-cartel, offtopic-gophers, the garrison, and the monster beverage company.\n\n## License\n\nthis code uses the MIT license, not that anybody cares. If you don't know, then don't sweat it.\n\nmade with ☕ by 🍩 😋 donuts-are-good\n\n\n😆👏 Thanks\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdonuts-are-good%2Fpuggy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdonuts-are-good%2Fpuggy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdonuts-are-good%2Fpuggy/lists"}