{"id":34188065,"url":"https://github.com/usama28232/candid","last_synced_at":"2026-03-09T23:35:26.907Z","repository":{"id":186910826,"uuid":"675976205","full_name":"usama28232/candid","owner":"usama28232","description":"A potential baseline application with generic API operation handling","archived":false,"fork":false,"pushed_at":"2023-08-09T18:32:16.000Z","size":32,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-06-20T21:19:36.658Z","etag":null,"topics":["basic-authentication","bcrypt","crud-application","golang","postgres","rest-api","uber-zap"],"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/usama28232.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}},"created_at":"2023-08-08T06:45:10.000Z","updated_at":"2023-09-09T18:06:17.000Z","dependencies_parsed_at":"2023-08-09T20:24:18.417Z","dependency_job_id":null,"html_url":"https://github.com/usama28232/candid","commit_stats":null,"previous_names":["usama28232/potential-candid-go","usama28232/candid"],"tags_count":0,"template":false,"template_full_name":"usama28232/authexample","purl":"pkg:github/usama28232/candid","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usama28232%2Fcandid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usama28232%2Fcandid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usama28232%2Fcandid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usama28232%2Fcandid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/usama28232","download_url":"https://codeload.github.com/usama28232/candid/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usama28232%2Fcandid/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30316772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T20:05:46.299Z","status":"ssl_error","status_checked_at":"2026-03-09T19:57:04.425Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["basic-authentication","bcrypt","crud-application","golang","postgres","rest-api","uber-zap"],"created_at":"2025-12-15T15:25:48.553Z","updated_at":"2026-03-09T23:35:26.898Z","avatar_url":"https://github.com/usama28232.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# candid\nA core framework to assist web api crud opertaions.\n\nPurpose of this project is to provide most a commonly used base-line application framework to assist development.\n\nThe goal is to eliminate repetitive tasks and focus on writing the business aspect.\n\n**PS:** `candid` is built on top of [this template](https://github.com/usama28232/authexample), hence it follows same setup, APIs and has a document on all \u003cu\u003ehow-to\u003c/u\u003e stuff if you are looking for it\n\n## Project Structure \u0026 Explanation\n\nThe project structure consists of a mixture of MVC \u0026 Service based architecture.\n\n\n### Controllers\n\nThis (package: `controllers`) will contain the default handlers for `GET`, `POST`, `DELETE`, etc requests. It exposes the following interface in `controller_base.go`\n\nIn other words: To make the most of it, a controller must follow this pattern.\n\n```\ntype controllerBase interface {\n\t// GET: \"\u003ccontext\u003e/\u003cbase_route\u003e\"\n\tGetAllHandler(http.ResponseWriter, *http.Request)\n\n\t// POST: \"\u003ccontext\u003e/\u003cbase_route\u003e\"\n\tCreateHandler(http.ResponseWriter, *http.Request)\n\n\t// GET: \"\u003ccontext\u003e/\u003cbase_route\u003e/\u003cvalue\u003e\"\n\tGetHandler(http.ResponseWriter, *http.Request)\n\n\t// DELETE: \"\u003ccontext\u003e/\u003cbase_route\u003e/\u003cvalue\u003e\"\n\tDeleteHandler(http.ResponseWriter, *http.Request)\n\n\tGetRouteModel() routes.RouteConfig\n}\n```\n\n`GetRouteModel` returns an 'derived' instance of controller's route configuration *- explained below*\n\nFor example, a simple controller definition `HelloController` would be:\n\n```\n...\ntype HelloController struct {\nmyControllerBase\n}\n\nfunc (c *HelloController) GetAllHandler(writer http.ResponseWriter, request *http.Request) {\n\tshared.EncodeResponse(hello.SayHello(), writer)\n}\n\nfunc (c *HelloController) DeleteHandler(w http.ResponseWriter, r *http.Request) {\n\tshared.EncodeResponse(\"Success\", w)\n}\n\nfunc (u *HelloController) GetRouteModel() routes.RouteConfig {\n\treturn \u0026hello.HelloRouteModel{}\n}\n...\n```\n\nThis is the engagement point of service layer code.\n\n...\n\n### Route-Model\n\nRouteModel (package: `routes`) is the basic skeleton of what a 'derived' controller is capable of, the above was all about the landing points. This area deals with the mapping between routes and landing points.\n\nDefault definition \u0026 Implementation from `RouteModel.go`\n\n```\n// Holds allowed endpoint configuration\ntype AllowedRoutes struct {\n\tAllowListAPI bool\n\tAllowAddAPI bool\n\tAllowDetailAPI bool\n\tAllowDeleteAPI bool\n}\n\ntype RouteConfig interface {\n\tInit() AllowedRoutes\n\tGetBaseRoute() (string, error)\n\tGetListRoute(RouteConfig) (string, error)\n\tGetAddRoute(RouteConfig) (string, error)\n\tGetInfoRoute(RouteConfig) (string, error)\n\tGetDeleteRoute(RouteConfig) (string, error)\n}\n\ntype RouteConfigImpl struct {\n}\n\nfunc (r *RouteConfigImpl) Init() AllowedRoutes {\n\treturn AllowedRoutes{AllowListAPI: false, AllowAddAPI: false, AllowDetailAPI: false, AllowDeleteAPI: false}\n}\n\nfunc (r *RouteConfigImpl) GetBaseRoute() (string, error) {\n\treturn r.RouteErrHandler()\n}\n\nfunc (r *RouteConfigImpl) GetListRoute(c RouteConfig) (string, error) {\n\treturn c.GetBaseRoute()\n}\n\nfunc (r *RouteConfigImpl) GetAddRoute(c RouteConfig) (string, error) {\n\treturn c.GetBaseRoute()\n}\n\nfunc (r *RouteConfigImpl) GetInfoRoute(c RouteConfig) (string, error) {\n\tv, err := c.GetBaseRoute()\n\treturn v + \"/{value:[A-Za-z0-9]+}\", err\n}\n\nfunc (r *RouteConfigImpl) GetDeleteRoute(c RouteConfig) (string, error) {\n\tv, err := c.GetBaseRoute()\n\treturn v + \"/{value:[A-Za-z0-9]+}\", err\n}\n\nfunc (r *RouteConfigImpl) RouteErrHandler() (string, error) {\n\treturn \"\", errors.New(\"error: route config not defined\")\n}\n```\n\nFrom this, you will be able to add a derived implementation by doing just this:\n\n```\ntype HelloRouteModel struct {\n\troutes.RouteConfigImpl\n}\n\nfunc (r *HelloRouteModel) Init() routes.AllowedRoutes {\n\treturn routes.AllowedRoutes{AllowListAPI: true, AllowDeleteAPI: true}\n}\n\nfunc (r *HelloRouteModel) GetBaseRoute() (string, error) {\n\treturn \"/hello\", nil\n}\n```\n\nThe above definition will tell the framework to map base route `/hello` to `HelloController` *- see above example*\n\nThe `Init` method returns the allowed endpoints configuration in the following format.\n\n```\ntype AllowedRoutes struct {\n\tAllowListAPI bool\n\tAllowAddAPI bool\n\tAllowDetailAPI bool\n\tAllowDeleteAPI bool\n}\n```\n\nThis is then evaluated during registration process of the framework\n\n```\n...\nfunc register(controller controllerBase, r *mux.Router) *mux.Router {\n\trm := controller.GetRouteModel()\n\tavailableRoutes := rm.Init()\n\tbaseRoute, baseRErr := rm.GetBaseRoute()\n\n\tif baseRErr == nil \u0026\u0026 availableRoutes.AllowListAPI {\n\t\tr.HandleFunc(baseRoute, controller.GetAllHandler).Methods(http.MethodGet)\n\t}\n\n\tif availableRoutes.AllowAddAPI {\n\t\taddRoute, addRErr := rm.GetAddRoute(rm)\n\t\tif addRErr == nil {\n\t\t\tr.HandleFunc(addRoute, controller.CreateHandler).Methods(http.MethodPost)\n\t\t}\n\t}\n\n\tif availableRoutes.AllowDetailAPI {\n\t\tgetInfoRoute, getInfoRErr := rm.GetInfoRoute(rm)\n\t\tif getInfoRErr == nil {\n\t\t\tr.HandleFunc(getInfoRoute, controller.GetHandler).Methods(http.MethodGet)\n\t\t}\n\t}\n\n\tif availableRoutes.AllowDeleteAPI {\n\t\tdelRoute, delRErr := rm.GetDeleteRoute(rm)\n\t\tif delRErr == nil {\n\t\t\tr.HandleFunc(delRoute, controller.DeleteHandler).Methods(http.MethodDelete)\n\t\t}\n\t}\n\treturn r\n}\n...\n```\n\nBecause of this architecture, router code is reduced to minimal\n\n```\nfunc RegisterRoutes() *mux.Router {\n\n\tmux := mux.NewRouter()\n\tmux.Use(authMiddleware)\n\thelloCont := \u0026HelloController{}\n\tuserCont := \u0026UserController{}\n\n\tmux = register(userCont, mux)\n\tmux = register(helloCont, mux)\n\n\tmux.StrictSlash(false)\n\treturn mux\n}\n```\n\nSo to add an endpoint, all you have to do is to add it's derived route-model and controller with required landings.\n\n\nTo add other custom endpoints, just provide \u003cu\u003eURL\u003c/u\u003e `string`, `http.HandlerFunc`, `*mux.Router` (Router instance) and `...string` (Array of allowed http methods)\n\n```\n\t...\n\tmux = registerCustom(\"/helloc\", helloCont.GetCustomLanding, mux, http.MethodGet, http.MethodDelete)\n\t...\n```\n\n```\nfunc (u *HelloController) GetCustomLanding(w http.ResponseWriter, request *http.Request) {\n\tw.Write([]byte(hello.SayHello()))\n}\n```\n\nThis way the custom endpoint `/helloc` with entertain `GET` \u0026 `DELETE` requests\n\n...\n\n### Service\n\nThis is the layer where business logic is supposed to live.\n\n*Currently, service files are identified by '\\\u003cmodel\\\u003e_service.go'*\n\nHere is an example of `HelloService`\n\n```\nfunc SayHello() string {\n\treturn \"Hello World!\"\n}\n```\nWhich will be called by derived the controller.\n\nThis layer is independent so it can cover all aspects because in some cases there are composite entities that do not require exposed endpoints but are crucial part of a bigger operation.\n\nFor advanced example, Follow `UserController`\n\n...\n\n### Logging\n\nLogging package holds application configured logger instances `AccessLogger` *(for recording http request logs in 'access_logs.txt')* \u0026 `AppLogger` *(for application level logging in 'logs.txt')*\n\nLogging level can be changed from `config.json`\n\n*For now supported logging levels are `info` or `debug`*\n\n```\n{\n    \"LOG_PROFILE\": \"debug\",\n\t....\n```\n\nLogger instance can be obtained by:\n\n```\n\t...\n\tlogger := logging.GetLogger()\n\trm := controller.GetRouteModel()\n\tavailableRoutes := rm.Init()\n\tbaseRoute, baseRErr := rm.GetBaseRoute()\n\tlogger.Infow(\"** Route Config **\", \"Route\", baseRoute, zap.Any(\"Available Routes\", availableRoutes))\n\n\tif baseRErr == nil \u0026\u0026 availableRoutes.AllowListAPI {\n\t\tr.HandleFunc(baseRoute, controller.GetAllHandler).Methods(http.MethodGet)\n\t\tlogger.Debugw(\"- Adding Route\", \"v\", baseRoute, \"m\", http.MethodGet)\n\t}\n\t....\n```\n\nThere are a couple of logging approaches we can take up, a detailed explanation in [this repository](https://github.com/usama28232/webservice#working-explanation)\n\nThis repository implements file/profile based approach\n\n...\n\n### Shared\n\nThis package contains utility functions, application-config, helper functions and global constants\n\n**PS:** This package will hold message resolver related code\n\n...\n\n### Auth\n\nThis package contains authentication helper functions\n\nBy default, all endpoints are protected by `Basic-Authentication`\n\nTo set an exception for a resource use the `NO_AUTH` string array in `routes_config.go`\n\n```\nvar NOAUTH = []string{\"/hello/*\"}\n```\n\nThis supports wildcard at the end of string\n\nTo change this to exact match just remove `*` from the end\n\n```\nvar NOAUTH = []string{\"/hello\"}\n```\n...\n\n## Conclusion\n\nThis framework makes development easier and robust.\nYou can clone and start writing business right away!\n\nFurthermore, I have plans to add `GORM` and provide basic crud functionality at service layer *(if possible)* like [java spring framework](https://github.com/usama28232/generic-operations)\n\n\n\n### Feel free to edit/expand/explore this repository\n\nFor feedback and queries, reach me on LinkedIn at [here](https://www.linkedin.com/in/usama28232/?original_referer=)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusama28232%2Fcandid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fusama28232%2Fcandid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusama28232%2Fcandid/lists"}