https://github.com/ivanceras/restq
Compacting the SQL into the URL rest API
https://github.com/ivanceras/restq
Last synced: 7 months ago
JSON representation
Compacting the SQL into the URL rest API
- Host: GitHub
- URL: https://github.com/ivanceras/restq
- Owner: ivanceras
- License: mit
- Created: 2020-01-06T08:48:44.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2024-04-12T18:49:30.000Z (over 1 year ago)
- Last Synced: 2025-05-16T21:24:23.971Z (8 months ago)
- Language: Rust
- Size: 448 KB
- Stars: 57
- Watchers: 4
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: Changelog.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# RestQ
[](https://crates.io/crates/restq)
The simplest way to express data operations in a rest API.
Implemented using the combination
of appropriate HTTP methods, url and csv for the data format.
Example:
Querying a simple table `person` with filtering, grouping and paging.
```
GET /person?age=lt.42&(student=eq.true|gender=eq.'M')&group_by=sum(age),grade,gender&having=min(age)=gt.42&order_by=age.desc,height.asc&page=20&page_size=100
```
This can then be converted into a SQL query.
```sql
SELECT * FROM person
WHERE age < 42
AND (student = true OR gender = 'M')
GROUP BY sum(age), grade, gender
HAVING min(age) > 42
ORDER BY age DESC, height ASC
LIMIT 100 OFFSET 1900 ROWS
```
The response body will contain a `csv` formatted data of the results from the query.
**RestQ Syntax/Grammar:**
```
create = ["CREATE" | "PUT"], "/", table, column_def_list, "\n", csv
select = "GET", "/", table, [join_table], column_list, [ "?", condition]
delete = "DELETE", table, [ "?", condition ]
update = ["UPDATE | "PATCH"] table, set_expr_list, [ "?", condition]
drop = ["DROP" | "DELETE"] "-", table
alter = ["ALTER" | "PATCH"] table, { drop_column | add_column | alter_column }
drop_column = "-", column
add_column = "+", column_def
alter_column = column, "=", column_def
column_def_list = "{", { column_def }, "}"
| "(", { column_def }, ")"
column_def = [ { column_attributes } ], column, [ "(" foreign ")" ], ":", data_type, [ "(" default_value ")" ]
column_attributes = primary | index | unique
primary = "*"
index = "@"
unique = "&"
data_type = "bool" | "s8" | "s16" | "s32" | "s64" | "u8" | "u16", etc
default_value = value
value = number | string | bool ,..etc
column = string, ".", string
| string
table = string
foreign = table
insert = table, column_list ,"\n", csv
column_list = "{", { column }, "}"
join_table = table, join_type, table
join_type = right_join | left_join | inner_join | full_join
right_join = "->"
left_join = "<-"
inner_join = "-><-"
full_join = "<-->"
condition = expr
expr = column | value | binary_operation
binary_operation = expr, operator, expr
operator = "and" | "or" | "eq" | "gte" | "lte" ,..etc
```
## Data types
- `bool` : boolean
- `s8` : u8 that autoincrements
- `s16` : u16 that autoincrements
- `s32` : u32 that autoincrements, serial
- `s64` : u64 that autoincrements, bigserial
- `f32` : float 4 bytes
- `f64` : float 8 bytes
- `i8`,`i16`,`i32`,`i64` : signed integer
- `u8`,`u16`,`u32`,`u64` : unsigned intergers
- `text` : utf8 string
- `uuid` : plain uuid, randomly generated when null
- `uuid_rand` : randomly generated uuid
- `uuid_slug` : create a new uuid and generate a url friend base64 string out of it.
- `utc` : timestamp with time zone in utc,
- `url` : url types
- `json` : json
- `bytes` : binary data
## Creating a table and inserting records in one request.
```
PUT /+product{*product_id:s32,name:text,created_by(users):u32,created:utc,is_active:bool}
Content-Type: text/csv; charset=UTF-8
1,go pro,1,2019-10-31 11:59:59.872,,true
2,shovel,1,2019-11-01 07:30:00.462,,false
```
- http method is `PUT`
- url is a `restq` syntax.
- body is `csv`
**The equivalent SQL:**
```sql
CREATE TABLE product (
product_id serial NOT NULL PRIMARY,
name character varying NOT NULL,
created_by integer NOT NULL REFERENCES users(user_id),
created timestamp with time zone NOT NULL DEFAULT now(),
is_active boolean NOT NULL
INSERT INTO product(product_id, name, created_by, is_active)
VALUES(
(1,'go pro',1,2019-10-31 11:59:59.872,DEFAULT,true)
(2,'shovel',1,2019-11-01 07:30:00.462,DEFAULT,false)
);
```
## Show the table definition
```
HEAD /product
```
## Show all tables
```
HEAD /
```
## Querying the records
```
GET /product{product_id,name}?is_active=eq.true&order_by=created.desc
```
```sql
SELECT product_id,name FROM product WHERE is_active = true ORDER BY created DESC
```
## Inserting records
```
POST /product{product_id,name,created_by,created,is_active}
1,go pro,1,2019-10-31 11:59:59.872,,true
2,shovel,1,2019-11-01 07:30:00.462,,false
```
```sql
INSERT INTO product(product_id, name, created_by, is_active)
VALUES(
(1,'go pro',1,2019-10-31 11:59:59.872,true)
(2,'shovel',1,2019-11-01 07:30:00.462,false)
);
```
## Insert with query
```
POST /user{user_id,name,person_id(GET/person{id}?person.name=name)}
1,TOM JONES,,
```
```sql
INSERT INTO user(user_id, name, person_id)
VALUES(1, 'TOM JONES', (SELECT person.id FROM person WHERE person.name='TOM JONES'));
```
## Updating records
```
PATCH /product{description="I'm the new description now"}?product_id=1
```
```sql
UPDATE product SET description = 'I\'m the new description now' WHERE product_id = 1;
```
## Bulk updating records
2 versions of the same record is passed, first is the original, the next is the updated one
```
PATCH /product{*product_id,name}
1,go pro,1,go pro hero4
2,shovel,2,slightly used shovel
```
```sql
UPDATE product SET name = 'go pro hero4' WHERE id = 1;
UPDATE product SET name = 'slightly used shovel' WHERE id = 2'
```
## Delete
```
DELETE /product?product_id=1
```
```sql
DELETE FROM product WHERE product_id = '1'
```
## Delete multiple
```
DELETE /product{product_id}
1
2
3
```
```sql
DELETE FROM product WHERE product_id IN ('1','2','3')
```
## Delete multiple, by name(no primary keys).
```
DELETE /product{name,is_active}
Go Pro,true
Shovel,true
Chair,true
```
```sql
DELETE FROM product WHERE name = 'Go Pro' AND is_active = 'true';
DELETE FROM product WHERE name = 'Shovel' AND is_active = 'true';
DELETE FROM product WHERE name = 'Chair' AND is_active = 'true';
```
## Delete all records of a table
```
DELETE /product
```
```sql
TRUNCATE product;
```
## Complex select (with joins)
```restq
GET /product<-users{product.*,users.user_name}?product_id=1&is_active=true&created=gt.2019-11-05T08:45:03.432
```
```sql
SELECT product.*, users.user_name
FROM product
LEFT JOIN users ON product.created_by = users.user_id
WHERE product_id = 1
AND is_active = true
AND created > '2019-11-05T08:45:03.432'
```
## Join tables
### Supported join types
- [X] INNER JOIN `table1-><-table2`
- OUTER JOIN
- [X] LEFT JOIN `table1<-table2`
- [X] RIGHT JOIN `table1->table2`
- [X] FULL JOIN `table1<-->table2`
## Prior crate and inspiration
- [inquerest](https://github.com/ivanceras/inquerest), in the works of porting to call this library.
- [postgrest](https://github.com/PostgREST/postgrest), restq differs syntax to postgrest, with focus on intuitive filter clause
#### Please support this project:
[](https://www.patreon.com/ivanceras)