{"id":17204032,"url":"https://github.com/ivanceras/restq","last_synced_at":"2025-06-13T17:07:36.387Z","repository":{"id":57660691,"uuid":"232060181","full_name":"ivanceras/restq","owner":"ivanceras","description":"Compacting the SQL into the URL rest API","archived":false,"fork":false,"pushed_at":"2024-04-12T18:49:30.000Z","size":459,"stargazers_count":57,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-16T21:24:23.971Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","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/ivanceras.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":null,"patreon":"ivanceras","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2020-01-06T08:48:44.000Z","updated_at":"2025-01-07T15:16:24.000Z","dependencies_parsed_at":"2022-09-06T07:30:25.938Z","dependency_job_id":"c0b8a676-717a-4224-8b8b-354f3a816049","html_url":"https://github.com/ivanceras/restq","commit_stats":{"total_commits":169,"total_committers":1,"mean_commits":169.0,"dds":0.0,"last_synced_commit":"3e019ed79c2bc48ec36331b7b37e3db55874b9cb"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanceras%2Frestq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanceras%2Frestq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanceras%2Frestq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivanceras%2Frestq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivanceras","download_url":"https://codeload.github.com/ivanceras/restq/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":256289767,"owners_count":22366164,"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":[],"created_at":"2024-10-15T02:20:20.210Z","updated_at":"2025-06-13T17:07:36.359Z","avatar_url":"https://github.com/ivanceras.png","language":"Rust","funding_links":["https://patreon.com/ivanceras","https://www.patreon.com/ivanceras"],"categories":[],"sub_categories":[],"readme":"# RestQ\n\n[![Latest Version](https://img.shields.io/crates/v/restq.svg)](https://crates.io/crates/restq)\n\nThe simplest way to express data operations in a rest API.\n\nImplemented using the combination\nof appropriate HTTP methods, url and csv for the data format.\n\n\nExample:\nQuerying a simple table `person` with filtering, grouping and paging.\n\n```\nGET /person?age=lt.42\u0026(student=eq.true|gender=eq.'M')\u0026group_by=sum(age),grade,gender\u0026having=min(age)=gt.42\u0026order_by=age.desc,height.asc\u0026page=20\u0026page_size=100\n```\n\nThis can then be converted into a SQL query.\n```sql\nSELECT * FROM person\nWHERE age \u003c 42\n    AND (student = true OR gender = 'M')\nGROUP BY sum(age), grade, gender\nHAVING min(age) \u003e 42\nORDER BY age DESC, height ASC\nLIMIT 100 OFFSET 1900 ROWS\n```\nThe response body will contain a `csv` formatted data of the results from the query.\n\n\n**RestQ Syntax/Grammar:**\n```\ncreate  = [\"CREATE\" | \"PUT\"], \"/\",  table, column_def_list, \"\\n\", csv\n\nselect = \"GET\", \"/\", table, [join_table], column_list, [ \"?\", condition]\n\ndelete = \"DELETE\", table, [ \"?\", condition ]\n\nupdate = [\"UPDATE | \"PATCH\"] table, set_expr_list, [ \"?\", condition]\n\ndrop = [\"DROP\" | \"DELETE\"] \"-\", table\n\nalter = [\"ALTER\" | \"PATCH\"] table, { drop_column | add_column | alter_column }\n\ndrop_column = \"-\", column\n\nadd_column = \"+\", column_def\n\nalter_column = column, \"=\", column_def\n\n\ncolumn_def_list =  \"{\", { column_def }, \"}\"\n        | \"(\", { column_def }, \")\"\n\ncolumn_def = [ { column_attributes } ], column, [ \"(\" foreign \")\" ], \":\", data_type, [ \"(\" default_value \")\" ]\n\ncolumn_attributes = primary | index | unique\n\nprimary = \"*\"\n\nindex = \"@\"\n\nunique = \"\u0026\"\n\ndata_type = \"bool\" | \"s8\" | \"s16\" | \"s32\" | \"s64\" | \"u8\" | \"u16\", etc\n\ndefault_value  = value\n\nvalue = number | string | bool ,..etc\n\ncolumn = string, \".\", string\n        | string\n\ntable = string\n\nforeign = table\n\ninsert = table, column_list ,\"\\n\", csv\n\ncolumn_list = \"{\", { column }, \"}\"\n\n\njoin_table = table, join_type, table\n\njoin_type = right_join | left_join | inner_join | full_join\n\nright_join = \"-\u003e\"\n\nleft_join = \"\u003c-\"\n\ninner_join = \"-\u003e\u003c-\"\n\nfull_join = \"\u003c--\u003e\"\n\ncondition = expr\n\nexpr =  column | value | binary_operation\n\nbinary_operation = expr, operator, expr\n\noperator = \"and\" | \"or\" | \"eq\" | \"gte\" | \"lte\" ,..etc\n```\n\n\n## Data types\n- `bool`                            : boolean\n- `s8`                              : u8 that autoincrements\n- `s16`                             : u16 that autoincrements\n- `s32`                             : u32 that autoincrements, serial\n- `s64`                             : u64 that autoincrements, bigserial\n- `f32`                             : float 4 bytes\n- `f64`                             : float 8 bytes\n- `i8`,`i16`,`i32`,`i64`            : signed integer\n- `u8`,`u16`,`u32`,`u64`            : unsigned intergers\n- `text`                            : utf8 string\n- `uuid`                            : plain uuid, randomly generated when null\n- `uuid_rand`                       : randomly generated uuid\n- `uuid_slug`                       : create a new uuid and generate a url friend base64 string out of it.\n- `utc`                             : timestamp with time zone in utc,\n- `url`                             : url types\n- `json`                            : json\n- `bytes`                           : binary data\n\n## Creating a table and inserting records in one request.\n```\nPUT /+product{*product_id:s32,name:text,created_by(users):u32,created:utc,is_active:bool}\nContent-Type: text/csv; charset=UTF-8\n\n1,go pro,1,2019-10-31 11:59:59.872,,true\n2,shovel,1,2019-11-01 07:30:00.462,,false\n```\n\n - http method is `PUT`\n - url is a `restq` syntax.\n - body is `csv`\n\n**The equivalent SQL:**\n```sql\nCREATE TABLE product (\n product_id serial NOT NULL PRIMARY,\n name character varying NOT NULL,\n created_by integer NOT NULL REFERENCES users(user_id),\n created timestamp with time zone NOT NULL DEFAULT now(),\n is_active boolean NOT NULL\n\nINSERT INTO product(product_id, name, created_by, is_active)\nVALUES(\n    (1,'go pro',1,2019-10-31 11:59:59.872,DEFAULT,true)\n    (2,'shovel',1,2019-11-01 07:30:00.462,DEFAULT,false)\n);\n```\n\n## Show the table definition\n```\nHEAD /product\n\n```\n\n## Show all tables\n```\nHEAD /\n```\n\n## Querying the records\n\n```\nGET /product{product_id,name}?is_active=eq.true\u0026order_by=created.desc\n```\n\n```sql\nSELECT product_id,name FROM product WHERE is_active = true ORDER BY created DESC\n```\n\n## Inserting records\n```\nPOST /product{product_id,name,created_by,created,is_active}\n1,go pro,1,2019-10-31 11:59:59.872,,true\n2,shovel,1,2019-11-01 07:30:00.462,,false\n```\n\n```sql\nINSERT INTO product(product_id, name, created_by, is_active)\nVALUES(\n    (1,'go pro',1,2019-10-31 11:59:59.872,true)\n    (2,'shovel',1,2019-11-01 07:30:00.462,false)\n);\n```\n\n## Insert with query\n```\nPOST /user{user_id,name,person_id(GET/person{id}?person.name=name)}\n1,TOM JONES,,\n```\n```sql\nINSERT INTO user(user_id, name, person_id)\nVALUES(1, 'TOM JONES', (SELECT person.id FROM person WHERE person.name='TOM JONES'));\n```\n\n## Updating records\n\n```\nPATCH /product{description=\"I'm the new description now\"}?product_id=1\n```\n```sql\nUPDATE product SET description = 'I\\'m the new description now' WHERE product_id = 1;\n```\n\n## Bulk updating records\n\n2 versions of the same record is passed, first is the original, the next is the updated one\n```\nPATCH /product{*product_id,name}\n1,go pro,1,go pro hero4\n2,shovel,2,slightly used shovel\n```\n```sql\nUPDATE product SET name = 'go pro hero4' WHERE id = 1;\nUPDATE product SET name = 'slightly used shovel' WHERE id = 2'\n```\n\n## Delete\n\n```\nDELETE /product?product_id=1\n```\n\n```sql\nDELETE FROM product WHERE product_id = '1'\n```\n\n## Delete multiple\n```\nDELETE /product{product_id}\n1\n2\n3\n```\n\n```sql\nDELETE FROM product WHERE product_id IN ('1','2','3')\n```\n\n## Delete multiple, by name(no primary keys).\n```\nDELETE /product{name,is_active}\nGo Pro,true\nShovel,true\nChair,true\n```\n```sql\nDELETE FROM product WHERE name = 'Go Pro' AND is_active = 'true';\nDELETE FROM product WHERE name = 'Shovel' AND is_active = 'true';\nDELETE FROM product WHERE name  = 'Chair' AND is_active = 'true';\n```\n\n## Delete all records of a table\n```\nDELETE /product\n```\n\n```sql\nTRUNCATE product;\n```\n\n## Complex select (with joins)\n\n```restq\nGET /product\u003c-users{product.*,users.user_name}?product_id=1\u0026is_active=true\u0026created=gt.2019-11-05T08:45:03.432\n```\n\n```sql\nSELECT product.*, users.user_name\n    FROM product\n        LEFT JOIN users ON product.created_by = users.user_id\n    WHERE product_id = 1\n        AND is_active = true\n        AND created \u003e '2019-11-05T08:45:03.432'\n```\n\n\n## Join tables\n\n ### Supported join types\n - [X] INNER JOIN  `table1-\u003e\u003c-table2`\n - OUTER JOIN\n      - [X] LEFT JOIN  `table1\u003c-table2`\n      - [X] RIGHT JOIN  `table1-\u003etable2`\n      - [X] FULL JOIN `table1\u003c--\u003etable2`\n\n## Prior crate and inspiration\n - [inquerest](https://github.com/ivanceras/inquerest), in the works of porting to call this library.\n - [postgrest](https://github.com/PostgREST/postgrest), restq differs syntax to postgrest, with focus on intuitive filter clause\n\n#### Please support this project:\n [![Become a patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/ivanceras)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanceras%2Frestq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivanceras%2Frestq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivanceras%2Frestq/lists"}