{"id":38521875,"url":"https://github.com/not-empty/grit-microframework-go","last_synced_at":"2026-01-17T06:42:10.216Z","repository":{"id":305922465,"uuid":"965180713","full_name":"not-empty/grit-microframework-go","owner":"not-empty","description":"GRIT - Go REST Interface Toolkit - is a minimalist microservice framework built in pure Go. Designed to simplify RESTful API development without sacrificing performance, it draws inspiration from ALA (Automatic Lumen API) while embracing Go's philosophy of simplicity, concurrency, and clarity.","archived":false,"fork":false,"pushed_at":"2026-01-13T17:46:01.000Z","size":1290,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-13T19:23:55.974Z","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":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/not-empty.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":"audit.sh","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":"2025-04-12T15:37:16.000Z","updated_at":"2025-11-07T11:32:39.000Z","dependencies_parsed_at":"2025-07-22T18:34:00.483Z","dependency_job_id":null,"html_url":"https://github.com/not-empty/grit-microframework-go","commit_stats":null,"previous_names":["not-empty/grit-microframework-go"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/not-empty/grit-microframework-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/not-empty%2Fgrit-microframework-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/not-empty%2Fgrit-microframework-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/not-empty%2Fgrit-microframework-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/not-empty%2Fgrit-microframework-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/not-empty","download_url":"https://codeload.github.com/not-empty/grit-microframework-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/not-empty%2Fgrit-microframework-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28502819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T04:31:57.058Z","status":"ssl_error","status_checked_at":"2026-01-17T04:31:45.816Z","response_time":85,"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":[],"created_at":"2026-01-17T06:42:09.542Z","updated_at":"2026-01-17T06:42:10.211Z","avatar_url":"https://github.com/not-empty.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GRIT – Go REST Interface Toolkit\n\nGRIT is a minimalist microservice framework built in pure Go. Designed to simplify RESTful API development without sacrificing performance, it draws inspiration from ALA while embracing Go's philosophy of simplicity, concurrency, and clarity.\n\n---\n\n## Quickstart\n\n### 1. Clone\n\n```bash\ngit clone https://github.com/not-empty/grit-microframework-go.git\n```\n\n### 2. Docker Compose\n\nBring up the service and its MySQL dependency:\n\n```bash\ndocker-compose up -d\n```\n\nEnter the running container:\n\n```bash\ndocker exec -it grit sh\n```\n\n### 3. Environment\n\nCopy `.env.example` to `.env` and adjust:\n\n```env\nAPP_ENV=local           # expose errors in response when \"local\"\nAPP_LOG=true            # enable HTTP access logs\nAPP_NO_AUTH=true        # disable auth (not for production)\nAPP_PORT=8001           # HTTP port\n\nDB_DRIVER=mysql         # only MySQL supported\nDB_HOST=grit-mysql\nDB_NAME=grit\nDB_USER=user\nDB_PASS=password\nDB_PORT=3306\nDB_MAX_CONN=100\nDB_MAX_IDLE=10\n\nDB_HOST_TEST=grit-mysql\nDB_NAME_TEST=grit\nDB_PASS_TEST=password\nDB_PORT_TEST=3306\nDB_USER_TEST=user\n\nJWT_APP_SECRET=secret   # JWT signing secret\nJWT_EXPIRE=900          # expiration seconds\nJWT_RENEW=600           # auto-renew threshold seconds\n```\n\nAlso copy `./config/tokens.json.example` → `./config/tokens.json` to configure valid tokens and contexts.\n\n---\n\n## Run the API\n\nRun GRIT with:\n\n```bash\ngo run main.go\n```\n\n## Endpoints\n\n| Method | Path                        | Description                                |\n| ------ | --------------------------- | -------------------------------------------|\n| POST   | `/example/add`              | Create a new record                        |\n| POST   | `/example/bulk`             | Fetch specific records by IDs              |\n| POST   | `/example/bulk_add`         | Create up to 25 records in the same request|\n| GET    | `/example/dead_detail/{id}` | Get a deleted record by ID                 |\n| GET    | `/example/dead_list`        | List deleted records (paginated)           |\n| DELETE | `/example/delete/{id}`      | Soft-delete a record by ID                 |\n| GET    | `/example/detail/{id}`      | Get an active record by ID                 |\n| PATCH  | `/example/edit/{id}`        | Update specific fields                     |\n| GET    | `/example/list`             | List active records (paginated)            |\n| GET    | `/example/list_one`         | List one record based on params            |\n| POST   | `/example/select_raw`       | Execute a predefined raw SQL query safely  |\n| PATCH  | `/example/undelete/{id}`    | Soft-Undelete a record by ID               |\n\n---\n\n## Authentication \u0026 Authorization\n\n1. **Generate a JWT**\n\n   ```bash\n   curl -i -X POST http://localhost:$APP_PORT/auth/generate \\\n     -H \"Content-Type: application/json\" \\\n     -H \"Context: \u003cyour-context\u003e\" \\\n     -d '{\"token\":\"\u003ctoken\u003e\",\"secret\":\"\u003csecret\u003e\"}'\n   ```\n\n   On success you get HTTP 204 with headers:\n\n   - `X-Token`: JWT\n   - `X-Expires`: expiration timestamp\n\n2. **Make API calls**\n   ```bash\n   curl -i GET http://localhost:$APP_PORT/example/list \\\n     -H \"Authorization: Bearer \u003cJWT\u003e\" \\\n     -H \"Context: \u003cyour-context\u003e\" \\\n     -H \"Accept: application/json\"\n   ```\n   Every valid response return your valid token or renews it if is needed, always in the headers:\n   - `X-Token`: new JWT\n   - `X-Expires`: new expiration\n\n---\n\n## Request \u0026 Response Headers\n\n| Header          | Description                   |\n| --------------- | ----------------------------- |\n| `X-Request-ID`  | Unique ULID for the request   |\n| `X-Profile`     | Profiling timer (seconds)     |\n| `X-Token`       | JWT token (on auth or renew)  |\n| `X-Expires`     | JWT expiration timestamp      |\n| `X-Page-Cursor` | Cursor for next page (string) |\n\n---\n\n## Pagination with Cursor\n\nBy default list endpoints return up to **25** (or less if you provide the ?limit=x) items and include an `X-Page-Cursor` header when more pages exist.\n\n1. **First page** (no cursor):\n\n   ```bash\n   curl -i GET \"http://localhost:$APP_PORT/example/list\" \\\n     -H \"Authorization: Bearer \u003cJWT\u003e\"\n   ```\n\n   ```http\n   HTTP/1.1 200 OK\n   X-Page-Cursor: eyJsYXN0X2lkIjo...   # opaque cursor\n   Content-Type: application/json\n   [\n     {\"id\":\"1\",\"name\":\"Alice\"},\n     ...\n   ]\n   ```\n\n2. **Next page**:\n   ```bash\n   curl -i GET \"http://localhost:$APP_PORT/example/list?page_cursor=\u003ccursor\u003e\" \\\n     -H \"Authorization: Bearer \u003cJWT\u003e\"\n   ```\n\nOnce fewer than **25** records (or the value setted with ?limit=) return, no `X-Page-Cursor` is emitted (end of list).\n\n---\n\n## Ordering \u0026 Field Selection\n\nWorks on list and list_one endpoints\n\n- **Order** by any column:\n  `?order_by=name\u0026order=desc`\n\n- **Select fields**: the default is all the fields\n  `?fields=id,name,created_at`\n\nExample:\n\n```bash\ncurl -i GET \"http://localhost:$APP_PORT/example/list?order_by=age\u0026order=asc\u0026fields=id,name\" \\\n  -H \"Authorization: Bearer \u003cJWT\u003e\"\n```\n\n---\n\n## Filtering\n\nWorks on list and list_one endpoints\n\nUse `filter` params:\n\n```\n?filter=age:eql:30\u0026filter=name:lik:John\n```\n\nSupported operators:\n\n- `eql` → `=`\n- `neq` → `!=`\n- `lik` → `LIKE` (contains)\n- `gt` → `\u003e`\n- `lt` → `\u003c`\n- `gte` → `\u003e=`\n- `lte` → `\u003c=`\n- `btw` → `BETWEEN` (value1,value2)\n- `nul` → `IS NULL`\n- `nnu` → `IS NOT NULL`\n- `in` → `IN` (comma list)\n\n---\n\n## Raw Selects\n\nAllows execution of pre-registered raw SQL queries with named parameters. Queries must be registered in your model.\n\n1 - Register queries in app/repository/models/\u003cdomain\u003e_raw.go:\n```golang\npackage models\n\nimport \"github.com/not-empty/grit-microframework-go/app/helper\"\n\nfunc init() {\n    helper.RegisterRawQueries(\"example\", map[string]string{\n        // key is query name, value is SQL template\n        \"count_active\": `\n          SELECT COUNT(1) AS total\n          FROM example\n          WHERE age = :age\n        `,\n    })\n}\n```\n\n2 - Request format:\n\n```bash\nPOST /example/select_raw\nContent-Type: application/json\n\n{\n  \"query\": \"count_active\",\n  \"params\": {\n    \"age\" : 22\n  }\n}\n```\n\n3 - Response:\n\n[200 OK] with JSON array of rows (each row is an object)\n\n[400 Bad Request] if query is unknown, parameters mismatch, or contains forbidden terms\n\n[500 Internal Server Error] on execution errors\n\n4 - Limitations \u0026 rules:\n\nAllowed: only SELECT or WITH statements\n\nDenied substrings: ;, --, /*, */\n\nDenied keywords: drop, alter, truncate, delete, update, insert, create, merge, replace, grant, revoke, commit, rollback, savepoint, lock, unlock, exec, call, use, set, limit, offset, join\n\nAll named parameters (e.g. :id) in the query must be provided in the params object, and no extra parameters are allowed.\n\nMaximum rows returned is 25 (hard-coded).\n\n## Generators\n\n- **New Domain** (with DDL in `./cmd/sql/{name}.sql`):\n  ```bash\n  cd cmd/domain\n  go run main.go -domain=name\n  ```\n\nGenerated files:\n\n- `app/repository/models/{name}_model.go`\n- `app/repository/models/{name}_raw.go`\n- `app/router/domains/{name}_domain.go`\n\n\u003e Generated code for new domains are test free since they are abstract of the basic implementations.\n\n- **Generate Route**:\n  ```bash\n  cd cmd/route\n  go run main.go -route=name\n  ```\n\nGenerated files:\n\n- `app/controller/{name}_controller.go`\n- `app/router/routes/{name}_router.go`\n\n\u003e Generated code for new routes will counts toward coverage—tests since they are new logic.\n\n## Adding a Record with a Manual ID\n\nYou can add a new record by sending a `POST` request to `/example/add`, or use the `/example/bulk_add` endpoint to add multiple records at once. By default, the API automatically generates a unique ID for each record.\n\nHowever, if you prefer to use a custom ID, you can include the `id` field in the request body. In that case, the API will use the provided ID and skip the automatic ID generation.\n\n## Validation\n\nYou can add validation in fields including the validation statement in models or in the fields comments in the DDL file before generating the domain:\n\nEither way, you need to use the validate statement from https://github.com/go-playground/validator and its options.\n\n\u003e You can add or change in the model just including or editing the validate statement and the choosed options on the selected fields:\n\n```golang\ntype Example struct {\n\tID        string           `json:\"id\"`\n\tName      string           `json:\"name\" validate:\"required,min=5\"`\n\tAge       int              `json:\"age\" validate:\"required,number,gt=0,lt=100\"`\n\tLastSeen  *helper.JSONTime `json:\"last_seen\"`\n\tLastLogin *helper.JSONTime `json:\"last_login\"`\n\tCreatedAt *time.Time       `json:\"created_at\"`\n\tUpdatedAt *time.Time       `json:\"updated_at\"`\n\tDeletedAt *time.Time       `json:\"deleted_at\"`\n}\n```\n\n\u003e Or you can add a comment -- validate in the sql DDL inside the cmd/sql folder and regerate the model (recommended):\n\n```sql\nCREATE TABLE example (\n  `id` CHAR(26) NOT NULL,\n  `name` TEXT NOT NULL, -- validate: \"min=5\" -- sanitize-html\n  `age` INT DEFAULT 0, -- validate: \"required,number,gt=0,lt=100\"\n  `last_seen` DATE DEFAULT NULL,\n  `last_login` DATETIME DEFAULT NULL,\n  `created_at` DATETIME DEFAULT NULL,\n  `updated_at` DATETIME DEFAULT NULL,\n  `deleted_at` DATETIME DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  KEY `idx_example_deleted_at` (`deleted_at`) USING BTREE\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;\n\n```\n\n\u003e PS: Do not validate DATE or DATETIME fields with the \"datetime\" validator, the internal datetime_helper will treat these validations in runtime and returns a 400 HTTP code error if receives any invalid formated date.\n\n## Default Behavior\n\n\u003e GRIT automatically detects and omits any model fields that have a database‐side DEFAULT clause when inserting new records and these fields are not present i the request. In practice, this means:\n\nIf in your SQL you have a field with default value like:\n\n```sql\n...\nstatus_name VARCHAR(20) NOT NULL DEFAULT 'active',\nnotes TEXT DEFAULT NULL,\n...\n```\n\nAnd if you make an \"/add\" request without informing these fields, they will be omitted in the insert query, letting the database use his DEFAULT VALUE.\n\n---\n\n## Testing \u0026 Coverage\n\nRun all tests with coverage:\n\n```bash\n./audit.sh\n```\n\nSee `./tests/coverage/coverage-unit.html` for details.\n\n---\n\nFor more request examples, see `./ops/curl.sh`. Suggestions and contributions welcome!\n\nIf you are using a REST API software like Insomnia or Postman you can import the `./ops/rest_environment.json` and `./ops/rest_collection.json` files.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnot-empty%2Fgrit-microframework-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnot-empty%2Fgrit-microframework-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnot-empty%2Fgrit-microframework-go/lists"}