{"id":38632932,"url":"https://github.com/sted/smoothdb","last_synced_at":"2026-01-17T09:01:25.184Z","repository":{"id":211469720,"uuid":"729226080","full_name":"sted/smoothdb","owner":"sted","description":"SmoothDB provides an automatic RESTful API to PostgreSQL databases","archived":false,"fork":false,"pushed_at":"2025-09-26T11:48:31.000Z","size":1364,"stargazers_count":28,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-26T12:27:38.053Z","etag":null,"topics":["api","database","go","golang","postgres","postgresql","rest"],"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/sted.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-08T17:05:18.000Z","updated_at":"2025-09-26T11:48:35.000Z","dependencies_parsed_at":"2024-02-27T14:27:30.541Z","dependency_job_id":"cff4bcf8-de36-4e82-a6a1-e456e33ed1ba","html_url":"https://github.com/sted/smoothdb","commit_stats":null,"previous_names":["sted/smoothdb"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/sted/smoothdb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sted%2Fsmoothdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sted%2Fsmoothdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sted%2Fsmoothdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sted%2Fsmoothdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sted","download_url":"https://codeload.github.com/sted/smoothdb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sted%2Fsmoothdb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28504596,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T06:57:29.758Z","status":"ssl_error","status_checked_at":"2026-01-17T06:56:03.931Z","response_time":85,"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":["api","database","go","golang","postgres","postgresql","rest"],"created_at":"2026-01-17T09:00:52.206Z","updated_at":"2026-01-17T09:01:25.114Z","avatar_url":"https://github.com/sted.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SmoothDB  ![Tests](https://github.com/sted/smoothdb/actions/workflows/tests.yml/badge.svg)\n\nSmoothDB provides a RESTful API to PostgreSQL databases.\n\nConfigured databases and schemas can be accessed and modified easily with a REST JSON-based interface.\n\nIt is mostly compatible with [PostgREST](https://postgrest.org/en/stable/), with which it shares many characteristics.\n\nThe main differences are:\n\n* SmoothDB is in development and beta quality. Prefer PostgREST for now, which is rock solid\n* SmoothDB is faster and has a lower CPU load \n* It is written in Go\n* Can be used both stand-alone and as a library (the main motivation for writing this)\n* It also supports DDL operations (create / alter / drop databases, tables, manage constraints, roles, etc)\n* Supports multiple databases with a single instance\n* It has an Admin UI web dashboard\n\nSee [TODO.md](TODO.md) for the many things to be completed.\nPlease create issues to let me know your priorities.\n\n## Getting started\n\n### Install\n\nSmoothDB can be installed using the pre-built binaries published on [github](https://github.com/sted/smoothdb/releases) for each of the supported platforms. Support on Windows is not yet well tested.\n\nIf you are on MacOS (or Linux) you can use Homebew to install the package:\n\n```\nbrew tap sted/tap\nbrew install smoothdb\n```\n\nIf you have Go installed, you can install SmoothDB using:\n\n```\ngo install github.com/sted/smoothdb@latest\n```\n\nTo test your installation type \n\n```\nsmoothdb -h\n```\n\n### Start\n\nStarting SmoothDB, it creates a configuration file named **config.jsonc** in the current directory, with default values:  edit it for further customizations (see [Configuration](#Configuration-file)).\n\nYou can configure the database instance for SmoothDb invoking\n\n```\nsmoothdb --initdb\n```\n\nDetails in [Database configuration](#Database-configuration).\n\n## API\n\nHere you find some examples for the API.\nFor more detailed information, see [PostgREST API](https://postgrest.org/en/stable/references/api.html).\n\nThe compability with PostgREST has also the great advantage of being able to use the many existing client libraries, starting from [postgrest-js](https://github.com/supabase/postgrest-js). See the [complete list of available client libraries](https://github.com/supabase/postgrest-js).\n\nThe default Content-Type is \"**application/json**\".\n\n### Authentication\n\nLike PostgREST (see [PostgREST Authentication](https://postgrest.org/en/stable/references/auth.html)), SmoothDB is designed to keep the database at the center of API security.\n\nTo make an authenticated request, the client must include an Authorization HTTP header with the value **Bearer \\\u003cjwt\\\u003e**, where **jwt** is a [Java Web Token](jwt.io). \n\nA valid JWT for SmoothDB must include at least the **role** claim in the payload:\n\n```json\n{\n    \"role\": \"user1\"\n}\n```\n\nTo generate a JWT for testing, you can use the generator at [jwt.io](jwt.io), using as a secret the same value configured in the configuration file for JWTSecret.\n\nBelow is an example of an authenticated API call:\n\n```http\nGET /test HTTP/1.1\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic3RlZCJ9.-XquFDiIKNq5t6iov2bOD5k_LljFfAN7LqRzeWVuv7k\n```\n\n#### Token Generation\n\nSmoothDB provides a `/token` endpoint that generates JWT tokens:\n\n```http\nPOST /token HTTP/1.1\nContent-Type: application/json\n\n{\n    \"email\": \"user@example.com\",\n    \"password\": \"your_password\"\n}\n```\n\nThe response contains the access token and related information:\n\n```json\n{\n    \"access_token\": \"eyJhbGciOiJIUzI1NiIsInR...\",\n    \"token_type\": \"Bearer\",\n    \"expires_in\": 3600,\n    \"expires_at\": 1683036800,\n    \"refresh_token\": \"aaaabbbbccccddddeeee\",\n    \"user\": {\n        \"aud\": \"authenticated\",\n        \"role\": \"authenticated\",\n        \"email\": \"user@example.com\"\n    }\n}\n```\n\nSmoothDB supports two authentication methods via the `LoginMode` configuration option:\n\n1. **Internal Authentication** (`LoginMode: \"db\"`), suitable for local development but not recommended for production. Credentials are verified against PostgreSQL database users. \n\n2. **Supabase Auth** (`LoginMode: \"gotrue\"`), recommended for production applications. Authentication is delegated to the Supabase Auth service specified in the `AuthURL` configuration.\n\n**Important Notes:**\n\n- No explicit authorization step is needed beyond providing the JWT token with each request\n- Both authentication methods require email and password for token generation through the `/token` endpoint\n\nWe will omit the Authorization header in the following examples.\n\n### Create a database\n```http\nPOST /admin/databases HTTP/1.1\n\n{ \"name\": \"testdb\" }\n```\n\n### Create a table\n\n```http\nPOST /admin/databases/testdb/tables HTTP/1.1\n\n{ \n    \"name\": \"test\",\n    \"columns\": [\n\t\t{\"name\": \"col1\", \"type\": \"text\", \"notnull\": true},\n\t\t{\"name\": \"col2\", \"type\": \"boolean\"},\n\t\t{\"name\": \"col3\", \"type\": \"integer\", \"default\": \"42\", \"constraints\": [\"CHECK (col3 \u003e 40)\"]},\n\t\t{\"name\": \"col4\", \"type\": \"timestamp\"},\n\t\t{\"name\": \"arr\", \"type\": \"integer[]\"},\n\t\t{\"name\": \"extra\", \"type\": \"json\"},\n\t\t{\"name\": \"duration\", \"type\": \"tsrange\"},\n\t\t{\"name\": \"other\", \"type\": \"text\", \"constraints\": [\"REFERENCES test (col1)\"]}\n\t],\n\t\"constraints\": [\"PRIMARY KEY (col1)\"]\n}\n```\n\n### Insert records\n\nInsert one record:\n\n```http\nPOST /api/testdb/test HTTP/1.1\n\n{\n\t\"col1\": \"\",\n\t\"col2\": false,\n\t\"extra\": {\n\t\t\"a\": \"pippo\",\n\t\t\"b\": 4444,\n\t\t\"c\": [1,2,3,\"d\"]\n\t},\n\t\"arr\": [1,2,3],\n\t\"duration\": \"['2022-12-31 11:00','2023-01-01 06:00']\"\n}\n```\n\nInsert multiple records:\n\n```http\nPOST /api/testdb/test HTTP/1.1\n\n[\n\t{ \"col1\": \"one\", \"col3\": 43},\n\t{ \"col1\": \"two\", \"col3\": 44}\n]\n```\n\n\u003e [!IMPORTANT]\n\u003e In these example we use the default configuration for SmoothDB.\n\u003e To have fully PostgREST API compliancy, you should have a configuration similar to:\n\u003e ```json \n\u003e{\n\u003e \t\"EnableAdminRoute\": false,\n\u003e \t\"BaseAPIURL\": \"\",\n\u003e \t\"ShortAPIURL\": true,\n\u003e \t\"Database.AllowedDatabases\": [\"testdb\"]\n\u003e} \n\u003e ```\n\u003e With these configurations the \"/admin\" is no longer accessible and \"/api/testdb/test...\" becomes simply \"/test...\".\n\n### Select records\n\n```http\nGET /api/testdb/test?col3=gt.42 HTTP/1.1\n```\n```json\n[\n  { \"col1\": \"one\", \"col2\": null, \"col3\": 43, \"col4\": null, \"arr\": null, \"extra\": null, \"duration\": null, \"other\": null },\n  { \"col1\": \"two\", \"col2\": null, \"col3\": 44, \"col4\": null, \"arr\": null, \"extra\": null, \"duration\": null, \"other\": null }\n]\n```\n\nMost operators in [PostgREST Operators](https://postgrest.org/en/stable/references/api/tables_views.html#operators) are supported.\n\nMore conditions can be combined with the **and**, **or**, **not** operators ('and' being the default):\n\n```http\nGET /api/testdb/people?grade=gte.90\u0026student=is.true\u0026or=(age.eq.14,not.and(age.gte.11,age.lte.17)) HTTP/1.1\n```\n\nUse the **select** parameter to specify which column to show:\n\n```http\nGET /api/testdb/test?select=col1,col3\u0026col3.gt=42 HTTP/1.1\n```\n```json\n[\n  { \"col1\": \"one\", \"col3\": 43 },\n  { \"col1\": \"two\", \"col3\": 44 }\n]\n```\n\nPagination is controlled with **limit** and **offset** query parameters:\n\n```http\nGET /api/testdb/pages?limit=15\u0026offset=30 HTTP/1.1\n```\n\nOften it is better to manage pagination \"out of band\", using the Range header:\n\n```http\nGET /api/testdb/pages HTTP/1.1\nRange-Unit: items\nRange: 30-44\n```\n\nIn both ways the response will be similar to:\n\n```http\nHTTP/1.1 200 OK\nRange-Unit: items\nContent-Range: 30-44/*\n```\n\nIf both limit or offset parameters and range are present, the latter has precedence.\n\n### Relationships\n\nYou can include related resources in a single API call.\n\nSmoothDB uses Foreign Keys to determine which tables can be joined together, allowing many-to-one, many-to-many, one-to-many and one-to-one relationships.\n\nTo make a request joining data from multiple tables, you use again the **select** parameter, specifying the additional tables and the required columns for each:\n\n```http\nGET /api/testdb/orders?select=id,amount,companies(name,category) HTTP/1.1\n```\n```json\n[\n  { \"id\": \"1234\", \"amount\": 1000 , \"companies\": { \"name\": \"audi\", \"category\": \"cars\"}},\n  { \"id\": \"5678\", \"amount\": 2000 , \"companies\": { \"name\": \"bmw\", \"category\": \"cars\"}}\n]\n```\n\nYou can use the **spread** operator to flatten the results:\n\n```http\nGET /projects?select=id,...clients(client_name:name) HTTP/1.1\n```\n```json\n[\n\t{\"id\":1,\"client_name\":\"Microsoft\"},\n\t{\"id\":2,\"client_name\":\"Microsoft\"},\n\t{\"id\":3,\"client_name\":\"Apple\"},\n\t{\"id\":4,\"client_name\":\"Apple\"},\n\t{\"id\":5,\"client_name\":null}\n]\n```\n\nYou can nest relationships on multiple levels.\n\n```http\nGET /api/testdb/clients?select=id,projects(id,tasks(id,name))\u0026projects.tasks.name=like.Design* HTTP/1.1\n```\n\n### Aggregate Functions\n\nSmoothDB supports aggregate functions for performing calculations on data sets, compatible with [PostgREST aggregate queries](https://postgrest.org/en/stable/references/api/aggregate_functions.html).\n\n#### Basic Aggregates\n\nSimple aggregates return a single result:\n\n```http\nGET /orders?select=amount.sum() HTTP/1.1\n```\n```json\n[{\"sum\":108000}]\n```\n\nSupported aggregate functions:\n- `avg()` - Average value\n- `count()` - Count of rows\n- `max()` - Maximum value  \n- `min()` - Minimum value\n- `sum()` - Sum of values\n\n#### Grouped Aggregates\n\nInclude non-aggregate fields to group results:\n\n```http\nGET /orders?select=amount.sum(),customer_id\u0026order=customer_id.asc HTTP/1.1\n```\n```json\n[\n  {\"sum\":36000,\"customer_id\":1},\n  {\"sum\":24000,\"customer_id\":2},\n  {\"sum\":48000,\"customer_id\":3}\n]\n```\n\n#### Custom Labels and Type Casting\n\n```http\nGET /orders?select=total_revenue:amount.sum(),avg_order:amount.avg()::int HTTP/1.1\n```\n```json\n[{\"total_revenue\":108000,\"avg_order\":36000}]\n```\n\n#### Count Without Specifying Field\n\n```http\nGET /orders?select=count() HTTP/1.1\n```\n```json\n[{\"count\":25}]\n```\n\n#### Aggregates with JSON Columns\n\n```http\nGET /orders?select=details-\u003etax::numeric.sum() HTTP/1.1\n```\n```json\n[{\"sum\":1250.50}]\n```\n\n#### Aggregates in Embedded Resources\n\n```http\nGET /customers?select=name,orders(amount.sum()) HTTP/1.1\n```\n```json\n[\n  {\"name\":\"Alice\",\"orders\":[{\"sum\":15000}]},\n  {\"name\":\"Bob\",\"orders\":[{\"sum\":22000}]}\n]\n```\n\n#### Configuration\n\nAggregate functions are enabled by default. To disable them, set `Database.AggregatesEnabled` to `false` in your configuration:\n\n```json\n{\n  \"Database\": {\n    \"AggregatesEnabled\": false\n  }\n}\n```\n\nWhen disabled, attempts to use aggregate functions will return an error.\n\n\n## Example for using SmoothDB in your application\n\nYou can embed SmoothDB functionalities in your backend app with relative ease.\n\nThis short example is a minimal app that exposes a **/products** GET route to obtain the JSON array of the products and a **/view** route to view them in a formatted HTML table.\n\nIn this note we omit error handling for brevity, see the whole example in [examples/server.go](examples/server.go).\n\n\u003e [!WARNING]\n\u003e While you can already be confident with the retro compatibility of the API, because of the goal of\n\u003e compatibility with PostgREST, this is not yet the case for the exported functions in the various\n\u003e packages.\n\n```go\nimport (\n\t...\n\t\n\t\"github.com/sted/heligo\"\n\t\"github.com/sted/smoothdb/api\"\n\t\"github.com/sted/smoothdb/database\"\n\tsmoothdb \"github.com/sted/smoothdb/server\"\n)\n\nfunc main() {\n\t// base configuration\n\tbaseConfig := map[string]any{\n\t\t\"Address\":                   \":8085\",\n\t\t\"AllowAnon\":                 true,\n\t\t\"BaseAPIURL\":                \"\",\n\t\t\"ShortAPIURL\":               true,\n\t\t\"Logging.FilePath\":          \"./example.log\",\n\t\t\"Database.AllowedDatabases\": []string{\"example\"},\n\t}\n\t// smoothdb initialization\n\ts, _ := smoothdb.NewServerWithConfig(baseConfig, nil)\n\t\n\t// -- here the database is connected and the standard routes are prepared\n\t\n\t// prepare db content\n\tprepareContent(s)\n\t// create template and a view route\n\tprepareView(s)\n\t// run\n\ts.Run()\n}\n```\nIn *prepareContent* we see the basic interactions with the database.\n\n```go\nfunc prepareContent(s *smoothdb.Server) error {\n\n\tdbe_ctx, _, _ := database.ContextWithDb(context.Background(), nil, \"postgres\")\n\t// create a database\n\tdb, _ := s.DBE.GetOrCreateActiveDatabase(dbe_ctx, \"example\")\n\tctx, _, err := database.ContextWithDb(context.Background(), db, \"postgres\")\n\t// delete previous table if exists\n\tdatabase.DeleteTable(ctx, \"products\", true)\n\t// create a table 'products'\n\tdatabase.CreateTable(ctx, \u0026database.Table{\n\t\tName: \"products\",\n\t\tColumns: []database.Column{\n\t\t\t{Name: \"name\", Type: \"text\"},\n\t\t\t{Name: \"price\", Type: \"int4\"},\n\t\t\t{Name: \"avail\", Type: \"bool\"},\n\t\t},\n\t\tIfNotExists: true,\n\t})\n\t// insert records\n\tdatabase.CreateRecords(ctx, \"products\", []database.Record{\n\t\t{\"name\": \"QuantumDrive SSD 256GB\", \"price\": 59, \"avail\": true},\n\t\t{\"name\": \"SolarGlow LED Lamp\", \"price\": 99, \"avail\": false},\n\t\t{\"name\": \"AquaPure Water Filter\", \"price\": 20, \"avail\": true},\n\t\t{\"name\": \"BreezeMax Portable Fan\", \"price\": 5, \"avail\": true},\n\t\t{\"name\": \"Everlast Smartwatch\", \"price\": 200, \"avail\": false},\n\t\t{\"name\": \"JavaPro Coffee Maker\", \"price\": 45, \"avail\": true},\n\t\t{\"name\": \"SkyView Drone\", \"price\": 150, \"avail\": true},\n\t\t{\"name\": \"EcoCharge Solar Charger\", \"price\": 30, \"avail\": false},\n\t\t{\"name\": \"GigaBoost WiFi Extender\", \"price\": 75, \"avail\": true},\n\t\t{\"name\": \"ZenSound Noise-Canceling Headphones\", \"price\": 10, \"avail\": false},\n\t}, nil)\n\t// grant read access to everyone\n\tdatabase.CreatePrivilege(ctx, \u0026database.Privilege{\n\t\tTargetName: \"products\",\n\t\tTargetType: \"table\",\n\t\tTypes:      []string{\"select\"},\n\t\tGrantee:    \"public\",\n\t})\n\treturn nil\n}\n```\n\nIn *prepareView* we create a standard html/template and register a route to view the content.\n\n```go\nfunc prepareView(s *smoothdb.Server) error {\n\t// create the template\n\tt, _ := template.New(\"\").Parse(`\n\t\t\u003chtml\u003e\n\t\t\u003chead\u003e\n\t\t\u003cstyle\u003e\n\t\t\ttable {\n\t\t\t\tmargin-left: auto;\n    \t\t\tmargin-right: auto;\n\t\t\t\tborder-collapse: collapse;\n\t\t\t\tborder: 2px solid rgb(200, 200, 200);\n\t\t\t\tletter-spacing: 1px;\n\t\t\t\tfont-family: sans-serif;\n\t\t\t\tfont-size: 0.8rem;\n\t\t\t}\n\t\t\tth {\n\t\t\t\tbackground-color: #3f87a6;\n\t\t\t\tcolor: #fff;\n\t\t  \t}\n\t\t\ttd {\n\t\t\t\tbackground-color: #e4f0f5;\n\t\t\t}\n\t\t\ttd,th {\n\t\t\t\tborder: 1px solid rgb(190, 190, 190);\n\t\t\t\tpadding: 5px 10px;\n\t\t\t}  \n\t\t\u003c/style\u003e\n\t\t\u003c/head\u003e\n\t\t\u003cbody\u003e\n\t\t\u003ch1\u003eProducts\u003c/h1\u003e\n\t\t\u003ctable\u003e\n\t\t\t\u003ctr\u003e\u003cth\u003eName\u003c/th\u003e\u003cth\u003ePrice\u003c/th\u003e\u003cth\u003eAvail\u003c/th\u003e\u003c/tr\u003e\n\t\t\t{{range .}}\n\t\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cb\u003e{{.Name}}\u003c/b\u003e\u003c/td\u003e\u003ctd\u003e{{.Price}}\u003c/td\u003e\u003ctd\u003e{{.Avail}}\u003c/td\u003e\n\t\t\t\t\u003c/tr\u003e\n\t\t\t{{end}}\n\t\t\u003c/table\u003e\n\t\t\u003c/body\u003e`)\n\t\n\t// register a route\n\tr := s.GetRouter()\n\tm := s.MiddlewareWithDbName(\"example\")\n\tg := r.Group(\"/view\", m)\n\tg.Handle(\"GET\", \"\", func(ctx context.Context, w http.ResponseWriter, r heligo.Request) (int, error) {\n\t\tresults, err := database.GetDynStructures(ctx, \"products\")\n\t\tif err != nil {\n\t\t\treturn api.WriteError(w, err)\n\t\t}\n\t\terr = t.Execute(w, results)\n\t\tif err == nil {\n\t\t\treturn http.StatusOK, nil\n\t\t} else {\n\t\t\treturn http.StatusInternalServerError, err\n\t\t}\n\t})\n\treturn nil\n}\n```\n\nTo try the example\n\n\tgo run server.go\n\nin the examples directory and browse to *localhost:8085/products* and *localhost:8085/view*.\n\n## Admin UI\n\n\u003e [!WARNING] \n\u003e Beta.\n\n![](/misc/screenshot.png)\n\nA simple interface for the basic administration commands.\n\nIt allows to configure databases, tables, colums, roles, etc., must be explicitly enabled and for now needs the configuration of the anonymous role to work.\n\nThese are the required configurations:\n\n```json \n{\n\t\"AllowAnon\": true,\n \t\"EnableAdminRoute\": true,\n\t\"EnableAdminUI\": true,\n} \n```\n\n## Plugins\n\n\u003e [!WARNING]\n\u003e This is experimental.\n\u003e\n\u003e It is inherently complicated to build plugins in Go, and it is normally advisable to compile them together with the host program code.\n\nAnother way to extend the capabilities of SmoothDB is through the plugin mechanism: the plugins are Go libraries that comply with the `plugins.Plugin` interface and are loaded when the server starts.\n\nCurrently, the plugins have access to the logger, the router, and the database. More granular interfaces between plugins and host will be created if deemed appropriate.\n\nIn the directory `plugins/plugins/example` there is a sample plugin:\n\n```go\ntype examplePlugin struct {\n\tlogger *logging.Logger\n\trouter *heligo.Router\n}\n\nfunc (p *examplePlugin) Prepare(h plugins.Host) error {\n\tp.logger = h.GetLogger()\n\tp.logger.Info().Msg(\"examplePlugin: Preparing\")\n\tp.router = h.GetRouter()\n\tp.router.Handle(\"GET\", \"/example\", func(c context.Context, w http.ResponseWriter, r heligo.Request) (int, error) {\n\t\tw.Write([]byte(\"Here we are\"))\n\t\treturn http.StatusOK, nil\n\t})\n\treturn nil\n}\n\nfunc (p *examplePlugin) Run() error {\n\tp.logger.Info().Msg(\"examplePlugin: Started\")\n\treturn nil\n}\n```\n\nTo build it use the following command:\n\n```\n\tgo build -trimpath -buildmode=plugin -o example.plugin main.go\n```\n\n## Configuration\n\nConfiguration parameters can be provided via configuration file, environment variables and command line, with increasing priority.\n\n### Configuration file\n\nThe configuration file *config.jsonc* (JSON with Comments) is created automatically on the first start, in the working directory. It contains the following parameters with their defaults:\n\n| Name | Description | Default |\n| --- | --- | --- |\n| Address | Server address and port | 0.0.0.0:4000 |\n| CertFile | TLS certificate file | \"\" |\n| KeyFile | TLS certificate key file | \"\" |\n| LoginMode | Login mode: \"none\", \"db\", \"gotrue\" | none |\n| AuthURL | URL of the external AuthN service | \"\" |\n| AllowAnon | Allow unauthenticated connections | false |\n| JWTSecret | Secret for JWT tokens | \"\" |\n| SessionMode | Session mode: \"none\", \"role\" | \"role\" |\n| EnableAdminRoute | Enable administration of databases and tables | false |\n| EnableAdminUI | Enable Admin dashboard | false |\n| EnableAPIRoute | Enable API access | true |\n| BaseAPIURL | Base URL for the API | \"/api\" |\n| ShortAPIURL | Skip database name in API URL. Database.AllowedDatabases must contain a single db | false |\n| BaseAdminURL | Base URL for the Admin API | \"/admin\" |\n| CORSAllowedOrigins | CORS Access-Control-Allow-Origin | [\"*\"] |\n| CORSAllowCredentials | CORS Access-Control-Allow-Credentials | false |\n| EnableDebugRoute | Enable debug access | false |\n| PluginDir | Plugins' directory | \"./_plugins\" |\n| Plugins | Ordered list of plugins | [] |\n| ReadTimeout | The maximum duration for reading the entire request, including the body (seconds) | 60 |\n| WriteTimeout | The maximum duration before timing out writes of the response (seconds) | 60 |\n| RequestMaxBytes | Max bytes allowed in requests, to limit the size of incoming request bodies (0 for unlimited) | 1048576 (1MB) |\n| Database.URL | Database URL as postgresql://user:pwd@host:port/database | \"\" |\n| Database.MinPoolConnections | Miminum connections per pool | 10 |\n| Database.MaxPoolConnections | Maximum connections per pool | 100 |\n| Database.AnonRole | Anonymous role | \"\" |\n| Database.AllowedDatabases | Allowed databases | [] for all |\n| Database.SchemaSearchPath | Schema search path | [] for Postgres search path |\n| Database.TransactionMode | General transaction mode for operations: \"none\", \"commit\", \"rollback\" | \"none\" |\n| Database.AggregatesEnabled | Enable aggregate functions | true |\n| Logging.Level | Log level: trace, debug, info, warn, error, fatal, panic | \"info\" |\n| Logging.FileLogging | Enable logging to file | true |\n| Logging.FilePath | File path for file-based logging | \"./smoothdb.log\" |\n| Logging.MaxSize | MaxSize is the maximum size in megabytes of the log file before it gets rotated | 25 |\n| Logging.MaxBackups |  MaxBackups is the maximum number of old log files to retain | 3 |\n| Logging.MaxAge | MaxAge is the maximum number of days to retain old log files | 5 |\n| Logging.Compress | True to compress old log files | false |\n| Logging.StdOut | Enable logging to stdout | false |\n| Logging.PrettyConsole | Enable pretty output for stdout | false |\n| Logging.ColorConsole | Enable colorful output for stdout | false |\n\n### Environment variables\n\n| Name | Description |\n| --- | --- | \n| SMOOTHDB_DATABASE_URL | Database.URL |\n| SMOOTHDB_JWT_SECRET | JWTSecret |\n| SMOOTHDB_AUTH_URL | AuthURL |\n| SMOOTHDB_ALLOW_ANON | AllowAnon |\n| SMOOTHDB_ENABLE_ADMIN_ROUTE | EnableAdminRoute | \n| SMOOTHDB_CORS_ALLOWED_ORIGINS | CORSAllowedOrigins (comma-separated list) |\n| SMOOTHDB_CORS_ALLOW_CREDENTIALS | CORSAllowCredentials (true/false) |\n| SMOOTHDB_DEBUG | true forces: AllowAnon: true, LoginMode: \"db\", EnableAdminRoute: true, EnableAdminUI: \"true\", Logging.Level: \"trace\", Logging.StdOut: true, EnableDebugRoute: true |\n\t\n### Command line parameters\n\nYou can pass some configuration parameters in the command line:\n\n```\n$ ./smoothdb -h\n\nUsage: smoothdb [options]\n\nServer Options:\n\t-a, --addr \u003chost\u003e                Bind to host address (default: '0.0.0.0:4000')\n\t-d, --dburl \u003curl\u003e                Database URL\t\n\t-c, --config \u003cfile\u003e              Configuration file (default: './config.jsonc')\n\t--initdb                         Initialize db interactively and exit\n\t-h, --help                       Show this message\n```\n\n### Database configuration\n\nThe way SmoothDB connect to PostgreSQL is through the Database.URL configuration:\n\t\n\tpostgresql://[user:password@]host:port[/database]\n\nThe specified user will be used as the **authenticator**, so it should be a user with limited privileges.\nThe authenticator must be able to login and should not \"inherits” the privileges of roles it is a member of.\n\n```sql\nCREATE ROLE auth LOGIN NOINHERIT\n```\n\nInvoking\n\n```\nsmoothdb --initdb\n```\n\nand following the prompt, is an easy way to initialize the role and other configurations\n\n## Health Check Endpoints\n\nSmoothDB provides two health check endpoints for monitoring and integration with load balancers, orchestrators, and monitoring systems:\n\n### GET /live\n\nReturns a 200 OK response if the server is running. This endpoint can be used for basic liveness probes.\n\nExample:\n```bash\ncurl -I \"http://localhost:8000/live\"\n```\n\nResponse:\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n```\n\n### GET /ready\n\nReturns a 200 OK response if the server is ready to handle requests. In the current implementation, this endpoint behaves the same as `/live`.\n\nExample:\n```bash\ncurl -I \"http://localhost:8000/ready\"\n```\n\nResponse:\n```\nHTTP/1.1 200 OK\nContent-Type: application/json\n```\n\nBoth endpoints return a JSON response body:\n```json\n{\n  \"status\": \"ok\"\n}\n```\n\n## Schema Cache Reload via PostgreSQL NOTIFY\n\nSmoothDB supports reloading the schema cache without restarting the server, similar to PostgREST's functionality. This feature uses PostgreSQL's LISTEN/NOTIFY mechanism to trigger schema cache reloads. The same system will be used soon also to reload configuration.\n\n### How it works\n\n1. SmoothDB listens on the `smoothdb` channel for notifications\n2. When a notification is received with the payload `reload schema`, it triggers a schema cache reload\n3. You can reload schema cache for all databases or specific databases\n\n### Usage\n\n#### Reload schema cache for all active databases\n\n```sql\nNOTIFY smoothdb, 'reload schema';\n```\n\n#### Reload schema cache for a specific database\n\n```sql\nNOTIFY smoothdb, 'reload schema mydatabase';\n```\n\n#### Using the helper function\n\nFor convenience, you can create a helper function in your database:\n\n```sql\n-- Create the helper function (run this once)\nCREATE OR REPLACE FUNCTION notify_schema_reload(database_name text DEFAULT NULL)\nRETURNS void AS $$\nBEGIN\n  IF database_name IS NULL THEN\n    -- Reload all databases\n    PERFORM pg_notify('smoothdb', 'reload schema');\n  ELSE\n    -- Reload specific database\n    PERFORM pg_notify('smoothdb', 'reload schema ' || database_name);\n  END IF;\nEND;\n$$ LANGUAGE plpgsql;\n\n-- Usage examples:\nSELECT notify_schema_reload();        -- Reload all databases\nSELECT notify_schema_reload('mydb');  -- Reload specific database\n```\n\n#### Automatic schema reload on DDL changes\n\nYou can set up automatic schema cache reload when DDL changes occur:\n\n```sql\n-- Create the event trigger function\nCREATE OR REPLACE FUNCTION auto_notify_schema_reload()\nRETURNS event_trigger AS $$\nBEGIN\n  -- Get the current database name\n  PERFORM pg_notify('smoothdb', 'reload schema ' || current_database());\nEND;\n$$ LANGUAGE plpgsql;\n\n-- Create the event trigger\nCREATE EVENT TRIGGER schema_change_trigger\nON ddl_command_end\nEXECUTE FUNCTION auto_notify_schema_reload();\n```\n\nThis will automatically reload the schema cache whenever DDL operations (CREATE, ALTER, DROP) are performed.\n\n### Notes\n\n- The listener automatically reconnects if the connection is lost\n- Schema reloads are performed asynchronously and do not block other operations\n- If a specific database is not found or not active, the reload request for that database is ignored\n\n## Development\n\nContributions are warmly welcomed in the form of Pull Requests and Issue reporting.\n\nSome areas needing particular attention:\n\n* Security\n* Completing features present in PostgREST (see [TODO.md](TODO.md))\n* Verifying compatibility\n* Documentation\n* Performances and benchmarks\n\n### Tests\n\nThere are three categories of tests: \n\n* Internal unit tests\n* API tests \n* PostgREST tests\n\nThe last ones are taken directly from the PostgREST project.\n\nTo launch all the tests:\n\n```\nmake test\n```\n\nTo initialize and reset PostgREST fixtures:\n\n```\nmake prepare-postgrest-tests\n```\n\n## Acknowledgments\n\nThis project owes a debt of gratitude to:\n\n* [pgx](https://github.com/jackc/pgx), upon whose solid foundations it is built\n* [PostgREST](https://github.com/PostgREST/postgrest), which served as an inspiration, particularly for its excellent APIs, documentation, and robust testing.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsted%2Fsmoothdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsted%2Fsmoothdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsted%2Fsmoothdb/lists"}