sql2api 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sql2api-0.1.0/LICENSE +21 -0
- sql2api-0.1.0/PKG-INFO +208 -0
- sql2api-0.1.0/README.md +167 -0
- sql2api-0.1.0/pyproject.toml +56 -0
- sql2api-0.1.0/setup.cfg +4 -0
- sql2api-0.1.0/sql2api/__init__.py +6 -0
- sql2api-0.1.0/sql2api/__main__.py +3 -0
- sql2api-0.1.0/sql2api/app.py +296 -0
- sql2api-0.1.0/sql2api/cli.py +65 -0
- sql2api-0.1.0/sql2api/config.py +62 -0
- sql2api-0.1.0/sql2api/engine.py +37 -0
- sql2api-0.1.0/sql2api/errors.py +8 -0
- sql2api-0.1.0/sql2api/formats.py +142 -0
- sql2api-0.1.0/sql2api/lib/h2-2.2.224.jar +0 -0
- sql2api-0.1.0/sql2api/openapi.py +127 -0
- sql2api-0.1.0/sql2api/runners.py +142 -0
- sql2api-0.1.0/sql2api/sqltools.py +156 -0
- sql2api-0.1.0/sql2api/store.py +257 -0
- sql2api-0.1.0/sql2api.egg-info/PKG-INFO +208 -0
- sql2api-0.1.0/sql2api.egg-info/SOURCES.txt +24 -0
- sql2api-0.1.0/sql2api.egg-info/dependency_links.txt +1 -0
- sql2api-0.1.0/sql2api.egg-info/entry_points.txt +2 -0
- sql2api-0.1.0/sql2api.egg-info/requires.txt +26 -0
- sql2api-0.1.0/sql2api.egg-info/top_level.txt +1 -0
- sql2api-0.1.0/tests/test_integration.py +151 -0
- sql2api-0.1.0/tests/test_sql2api.py +483 -0
sql2api-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Anantha Raju C
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sql2api-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sql2api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Turn SQL into a REST API: run queries against MySQL, PostgreSQL, ClickHouse, SQLite or H2 over HTTP and get JSON, CSV, XML, YAML or XLSX back.
|
|
5
|
+
Author-email: Anantha Raju C <arcswdev@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AnanthaRajuC/SQL2API
|
|
8
|
+
Project-URL: Issues, https://github.com/AnanthaRajuC/SQL2API/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/AnanthaRajuC/SQL2API/blob/main/CHANGELOG.md
|
|
10
|
+
Keywords: sql,api,rest,flask,database,mysql,postgresql,clickhouse,sqlite,h2
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Framework :: Flask
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Database
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: Flask>=2.2
|
|
22
|
+
Requires-Dist: PyYAML>=6.0
|
|
23
|
+
Requires-Dist: openpyxl>=3.1
|
|
24
|
+
Provides-Extra: mysql
|
|
25
|
+
Requires-Dist: mysql-connector-python>=8.0; extra == "mysql"
|
|
26
|
+
Provides-Extra: postgres
|
|
27
|
+
Requires-Dist: psycopg2-binary>=2.9; extra == "postgres"
|
|
28
|
+
Provides-Extra: clickhouse
|
|
29
|
+
Requires-Dist: clickhouse-driver>=0.2; extra == "clickhouse"
|
|
30
|
+
Provides-Extra: h2
|
|
31
|
+
Requires-Dist: JayDeBeApi>=1.2; extra == "h2"
|
|
32
|
+
Requires-Dist: JPype1>=1.4; extra == "h2"
|
|
33
|
+
Provides-Extra: all
|
|
34
|
+
Requires-Dist: sql2api[clickhouse,h2,mysql,postgres]; extra == "all"
|
|
35
|
+
Provides-Extra: server
|
|
36
|
+
Requires-Dist: gunicorn>=21; extra == "server"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: sql2api[all]; extra == "dev"
|
|
39
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
# SQL2API
|
|
43
|
+
|
|
44
|
+
[](https://github.com/AnanthaRajuC/SQL2API/actions/workflows/ci.yml)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+

|
|
47
|
+
|
|
48
|
+
**Turn SQL into a REST API.** SQL2API is a small Flask service that runs SQL against your databases and returns the
|
|
49
|
+
results as JSON, NDJSON, CSV, TSV, XML, YAML or Excel. Save a query once and it becomes an endpoint with typed,
|
|
50
|
+
injection-safe parameters, versioning and run history.
|
|
51
|
+
|
|
52
|
+
~~~bash
|
|
53
|
+
$ curl 'http://127.0.0.1:5000/q/actor_by_id?id=7'
|
|
54
|
+
[{"actor_id": 7, "first_name": "GRACE", "last_name": "MOSTEL"}]
|
|
55
|
+
|
|
56
|
+
$ curl 'http://127.0.0.1:5000/q/films_by_rating?rating=PG&max_length=60&format=csv&page_size=2'
|
|
57
|
+
film_id,title,rating,length
|
|
58
|
+
410,HEAVEN FREEDOM,PG,48
|
|
59
|
+
443,HURRICANE AFFAIR,PG,49
|
|
60
|
+
~~~
|
|
61
|
+
|
|
62
|
+
| Database | JSON | NDJSON | XML | YAML | CSV | TSV | XLSX |
|
|
63
|
+
|------------|:----:|:------:|:---:|:----:|:---:|:---:|:----:|
|
|
64
|
+
| MySQL | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
65
|
+
| PostgreSQL | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
66
|
+
| ClickHouse | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
67
|
+
| SQLite | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
68
|
+
| H2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
- **Ad-hoc queries** - `POST /execute_sql` with SQL and a connection name.
|
|
73
|
+
- **Saved, versioned queries** - every save creates a new version; `GET /q/<name>?id=7` runs the latest one
|
|
74
|
+
(or `?version=1`). Each run is recorded in the query's execution history.
|
|
75
|
+
- **Bound parameters** - write `WHERE id = :id` and the value is sent to the database separately from the SQL, so it
|
|
76
|
+
cannot inject anything. Declare types (`{"id": "int"}`) and query-string values are converted for you.
|
|
77
|
+
- **Pagination** - `?page=2&page_size=50`, with `X-Has-More` telling you whether another page exists.
|
|
78
|
+
- **Read-only by default** - only single `SELECT`/`WITH`/`SHOW`/`DESCRIBE`/`EXPLAIN` statements run, and sessions are
|
|
79
|
+
opened read-only where the database supports it.
|
|
80
|
+
- **Secrets stay out of files** - `"password": "${PG_PASSWORD}"` in `db_connections.json` reads the environment.
|
|
81
|
+
- **Self-documenting** - OpenAPI at `/openapi.json`, Swagger UI at `/docs`.
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
~~~bash
|
|
86
|
+
pip install "sql2api[postgres]" # pick the drivers you need: mysql, postgres, clickhouse, h2
|
|
87
|
+
# or everything: pip install "sql2api[all]"
|
|
88
|
+
~~~
|
|
89
|
+
|
|
90
|
+
SQLite needs no extra driver. H2 also needs a Java runtime (the H2 JDBC jar is bundled).
|
|
91
|
+
From a clone: `pip install -e ".[dev]"`. Or use Docker - see [below](#docker).
|
|
92
|
+
|
|
93
|
+
## Quick start
|
|
94
|
+
|
|
95
|
+
The repository ships two sample SQLite databases and a couple of saved queries:
|
|
96
|
+
|
|
97
|
+
~~~bash
|
|
98
|
+
cd examples
|
|
99
|
+
cp db_connections.example.json db_connections.json
|
|
100
|
+
sql2api serve # http://127.0.0.1:5000
|
|
101
|
+
~~~
|
|
102
|
+
|
|
103
|
+
~~~bash
|
|
104
|
+
curl -X POST 'http://127.0.0.1:5000/execute_sql?page_size=3' -H 'Content-Type: application/json' \
|
|
105
|
+
-d '{"sql": "SELECT * FROM actor WHERE actor_id > :min", "params": {"min": 10}, "connection_name": "sakila-sqlite"}'
|
|
106
|
+
~~~
|
|
107
|
+
|
|
108
|
+
Open <http://127.0.0.1:5000/docs> for the interactive API reference.
|
|
109
|
+
|
|
110
|
+
For your own databases, run `sql2api init` in an empty folder: it creates `db_connections.json` (inactive templates for
|
|
111
|
+
every supported database) and `saved_sql/`. Edit the file, set `"active": true`, and start the server there.
|
|
112
|
+
|
|
113
|
+
## Saving a query as an endpoint
|
|
114
|
+
|
|
115
|
+
~~~bash
|
|
116
|
+
curl -X PATCH http://127.0.0.1:5000/save_sql_to_file -H 'Content-Type: application/json' -d '{
|
|
117
|
+
"filename": "actor_by_id",
|
|
118
|
+
"sql_query": "SELECT * FROM actor WHERE actor_id = :id",
|
|
119
|
+
"query_parameters": {"id": "int"},
|
|
120
|
+
"connection_name": "sakila-sqlite",
|
|
121
|
+
"author": "me", "description": "Look up an actor"
|
|
122
|
+
}'
|
|
123
|
+
|
|
124
|
+
curl 'http://127.0.0.1:5000/q/actor_by_id?id=7&format=yaml'
|
|
125
|
+
~~~
|
|
126
|
+
|
|
127
|
+
Saving again under the same name adds version 2; `DELETE /saved_sql/actor_by_id?version=1` removes one version.
|
|
128
|
+
|
|
129
|
+
## Configuration
|
|
130
|
+
|
|
131
|
+
Everything is configured through environment variables (all optional):
|
|
132
|
+
|
|
133
|
+
| Variable | Default | Effect |
|
|
134
|
+
|----------|---------|--------|
|
|
135
|
+
| `SQL2API_HOME` | current directory | Folder holding `db_connections.json` and `saved_sql/`. |
|
|
136
|
+
| `SQL2API_ALLOW_WRITES` | off | Allow `INSERT`/`UPDATE`/DDL. Otherwise only single read-only statements are accepted. |
|
|
137
|
+
| `SQL2API_API_KEY` | unset | When set, every request (except `/health` and `/docs`) needs a matching `X-API-Key` header. |
|
|
138
|
+
| `SQL2API_MAX_PAGE_SIZE` | `1000` | Upper limit for `page_size`. |
|
|
139
|
+
| `SQL2API_HOST` / `SQL2API_PORT` | `127.0.0.1` / `5000` | Bind address for `sql2api serve`. |
|
|
140
|
+
| `SQL2API_DEBUG` | off | Flask debug mode. Never enable on a reachable host. |
|
|
141
|
+
| `SQL2API_H2_JAR` | bundled | Path to a different H2 JDBC jar. |
|
|
142
|
+
|
|
143
|
+
## Security
|
|
144
|
+
|
|
145
|
+
SQL2API runs whatever SQL it is given against your databases, so it ships locked down and expects you to finish the job:
|
|
146
|
+
|
|
147
|
+
- Set `SQL2API_API_KEY` and serve over TLS (put it behind a reverse proxy).
|
|
148
|
+
- Connect with a database account that only has the privileges the API needs - the read-only guard is
|
|
149
|
+
defence in depth, not a replacement for grants. (H2's driver cannot enforce read-only, so H2 relies on the guard.)
|
|
150
|
+
- Use bound `:name` parameters. The older `{name}` placeholders paste text into the SQL and are therefore restricted
|
|
151
|
+
to numbers and plain text.
|
|
152
|
+
- Saved-query files are only read from `saved_sql/`; passwords are never returned by the API.
|
|
153
|
+
|
|
154
|
+
See [SECURITY.md](SECURITY.md) to report a vulnerability.
|
|
155
|
+
|
|
156
|
+
## Docker
|
|
157
|
+
|
|
158
|
+
~~~bash
|
|
159
|
+
docker build -t sql2api . # add --build-arg WITH_H2=true for H2 support
|
|
160
|
+
docker run -p 5000:5000 -v "$PWD/data:/data" -e SQL2API_API_KEY=change-me sql2api
|
|
161
|
+
~~~
|
|
162
|
+
|
|
163
|
+
The container keeps `db_connections.json` and `saved_sql/` in `/data`. It runs gunicorn with a single worker
|
|
164
|
+
(the files are protected by an in-process lock).
|
|
165
|
+
|
|
166
|
+
## API overview
|
|
167
|
+
|
|
168
|
+
| Endpoint | Method | Purpose |
|
|
169
|
+
|----------|--------|---------|
|
|
170
|
+
| `/execute_sql` | POST | Run ad-hoc SQL (`sql`, `connection_name`, optional `params`). |
|
|
171
|
+
| `/q/<name>` | GET, POST | Run a saved query; query-string or body values become parameters. |
|
|
172
|
+
| `/save_sql_to_file` | PATCH | Save a query (creates the next version). |
|
|
173
|
+
| `/list_files` | GET | List saved queries and their versions (`sort_by`, `sort_order`). |
|
|
174
|
+
| `/saved_sql/<name>` | DELETE | Delete a saved query or one `?version=`. |
|
|
175
|
+
| `/view_file_content` | GET | Raw content of a saved query file. |
|
|
176
|
+
| `/execute_sql_from_file`, `/execute_sql_with_parameters_from_file` | POST | Run a saved query by `filepath` (same as `/q/<name>`). |
|
|
177
|
+
| `/connections` | GET, PATCH | List (passwords masked) / add / update connections. |
|
|
178
|
+
| `/connections/<name>` | DELETE | Remove a connection. |
|
|
179
|
+
| `/health`, `/docs`, `/openapi.json` | GET | Liveness, Swagger UI, OpenAPI spec. |
|
|
180
|
+
|
|
181
|
+
Full details are in [documentation/API.md](documentation/API.md).
|
|
182
|
+
|
|
183
|
+
## Development
|
|
184
|
+
|
|
185
|
+
~~~bash
|
|
186
|
+
pip install -e ".[dev]"
|
|
187
|
+
ruff check .
|
|
188
|
+
python -m unittest discover -s tests -t .
|
|
189
|
+
~~~
|
|
190
|
+
|
|
191
|
+
The integration tests in `tests/test_integration.py` run against real MySQL, PostgreSQL, ClickHouse and H2 servers when
|
|
192
|
+
the matching `SQL2API_IT_*` variables are set, and are skipped otherwise; CI runs them against service containers.
|
|
193
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the pull request process, and [CHANGELOG.md](CHANGELOG.md) for what changed.
|
|
194
|
+
|
|
195
|
+
## Third-party components
|
|
196
|
+
|
|
197
|
+
The wheel bundles the [H2 Database](https://h2database.com) JDBC driver (MPL 2.0 / EPL 1.0). The sample SQLite
|
|
198
|
+
databases in `examples/` derive from the Sakila and Chinook sample datasets.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
[MIT](LICENSE) © Anantha Raju C
|
|
203
|
+
|
|
204
|
+
## Contact
|
|
205
|
+
|
|
206
|
+
Anantha Raju C - [@anantharajuc](https://twitter.com/anantharajuc) - arcswdev@gmail.com
|
|
207
|
+
|
|
208
|
+
Project link: <https://github.com/AnanthaRajuC/SQL2API>
|
sql2api-0.1.0/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# SQL2API
|
|
2
|
+
|
|
3
|
+
[](https://github.com/AnanthaRajuC/SQL2API/actions/workflows/ci.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
**Turn SQL into a REST API.** SQL2API is a small Flask service that runs SQL against your databases and returns the
|
|
8
|
+
results as JSON, NDJSON, CSV, TSV, XML, YAML or Excel. Save a query once and it becomes an endpoint with typed,
|
|
9
|
+
injection-safe parameters, versioning and run history.
|
|
10
|
+
|
|
11
|
+
~~~bash
|
|
12
|
+
$ curl 'http://127.0.0.1:5000/q/actor_by_id?id=7'
|
|
13
|
+
[{"actor_id": 7, "first_name": "GRACE", "last_name": "MOSTEL"}]
|
|
14
|
+
|
|
15
|
+
$ curl 'http://127.0.0.1:5000/q/films_by_rating?rating=PG&max_length=60&format=csv&page_size=2'
|
|
16
|
+
film_id,title,rating,length
|
|
17
|
+
410,HEAVEN FREEDOM,PG,48
|
|
18
|
+
443,HURRICANE AFFAIR,PG,49
|
|
19
|
+
~~~
|
|
20
|
+
|
|
21
|
+
| Database | JSON | NDJSON | XML | YAML | CSV | TSV | XLSX |
|
|
22
|
+
|------------|:----:|:------:|:---:|:----:|:---:|:---:|:----:|
|
|
23
|
+
| MySQL | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
24
|
+
| PostgreSQL | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
25
|
+
| ClickHouse | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
26
|
+
| SQLite | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
27
|
+
| H2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- **Ad-hoc queries** - `POST /execute_sql` with SQL and a connection name.
|
|
32
|
+
- **Saved, versioned queries** - every save creates a new version; `GET /q/<name>?id=7` runs the latest one
|
|
33
|
+
(or `?version=1`). Each run is recorded in the query's execution history.
|
|
34
|
+
- **Bound parameters** - write `WHERE id = :id` and the value is sent to the database separately from the SQL, so it
|
|
35
|
+
cannot inject anything. Declare types (`{"id": "int"}`) and query-string values are converted for you.
|
|
36
|
+
- **Pagination** - `?page=2&page_size=50`, with `X-Has-More` telling you whether another page exists.
|
|
37
|
+
- **Read-only by default** - only single `SELECT`/`WITH`/`SHOW`/`DESCRIBE`/`EXPLAIN` statements run, and sessions are
|
|
38
|
+
opened read-only where the database supports it.
|
|
39
|
+
- **Secrets stay out of files** - `"password": "${PG_PASSWORD}"` in `db_connections.json` reads the environment.
|
|
40
|
+
- **Self-documenting** - OpenAPI at `/openapi.json`, Swagger UI at `/docs`.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
~~~bash
|
|
45
|
+
pip install "sql2api[postgres]" # pick the drivers you need: mysql, postgres, clickhouse, h2
|
|
46
|
+
# or everything: pip install "sql2api[all]"
|
|
47
|
+
~~~
|
|
48
|
+
|
|
49
|
+
SQLite needs no extra driver. H2 also needs a Java runtime (the H2 JDBC jar is bundled).
|
|
50
|
+
From a clone: `pip install -e ".[dev]"`. Or use Docker - see [below](#docker).
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
The repository ships two sample SQLite databases and a couple of saved queries:
|
|
55
|
+
|
|
56
|
+
~~~bash
|
|
57
|
+
cd examples
|
|
58
|
+
cp db_connections.example.json db_connections.json
|
|
59
|
+
sql2api serve # http://127.0.0.1:5000
|
|
60
|
+
~~~
|
|
61
|
+
|
|
62
|
+
~~~bash
|
|
63
|
+
curl -X POST 'http://127.0.0.1:5000/execute_sql?page_size=3' -H 'Content-Type: application/json' \
|
|
64
|
+
-d '{"sql": "SELECT * FROM actor WHERE actor_id > :min", "params": {"min": 10}, "connection_name": "sakila-sqlite"}'
|
|
65
|
+
~~~
|
|
66
|
+
|
|
67
|
+
Open <http://127.0.0.1:5000/docs> for the interactive API reference.
|
|
68
|
+
|
|
69
|
+
For your own databases, run `sql2api init` in an empty folder: it creates `db_connections.json` (inactive templates for
|
|
70
|
+
every supported database) and `saved_sql/`. Edit the file, set `"active": true`, and start the server there.
|
|
71
|
+
|
|
72
|
+
## Saving a query as an endpoint
|
|
73
|
+
|
|
74
|
+
~~~bash
|
|
75
|
+
curl -X PATCH http://127.0.0.1:5000/save_sql_to_file -H 'Content-Type: application/json' -d '{
|
|
76
|
+
"filename": "actor_by_id",
|
|
77
|
+
"sql_query": "SELECT * FROM actor WHERE actor_id = :id",
|
|
78
|
+
"query_parameters": {"id": "int"},
|
|
79
|
+
"connection_name": "sakila-sqlite",
|
|
80
|
+
"author": "me", "description": "Look up an actor"
|
|
81
|
+
}'
|
|
82
|
+
|
|
83
|
+
curl 'http://127.0.0.1:5000/q/actor_by_id?id=7&format=yaml'
|
|
84
|
+
~~~
|
|
85
|
+
|
|
86
|
+
Saving again under the same name adds version 2; `DELETE /saved_sql/actor_by_id?version=1` removes one version.
|
|
87
|
+
|
|
88
|
+
## Configuration
|
|
89
|
+
|
|
90
|
+
Everything is configured through environment variables (all optional):
|
|
91
|
+
|
|
92
|
+
| Variable | Default | Effect |
|
|
93
|
+
|----------|---------|--------|
|
|
94
|
+
| `SQL2API_HOME` | current directory | Folder holding `db_connections.json` and `saved_sql/`. |
|
|
95
|
+
| `SQL2API_ALLOW_WRITES` | off | Allow `INSERT`/`UPDATE`/DDL. Otherwise only single read-only statements are accepted. |
|
|
96
|
+
| `SQL2API_API_KEY` | unset | When set, every request (except `/health` and `/docs`) needs a matching `X-API-Key` header. |
|
|
97
|
+
| `SQL2API_MAX_PAGE_SIZE` | `1000` | Upper limit for `page_size`. |
|
|
98
|
+
| `SQL2API_HOST` / `SQL2API_PORT` | `127.0.0.1` / `5000` | Bind address for `sql2api serve`. |
|
|
99
|
+
| `SQL2API_DEBUG` | off | Flask debug mode. Never enable on a reachable host. |
|
|
100
|
+
| `SQL2API_H2_JAR` | bundled | Path to a different H2 JDBC jar. |
|
|
101
|
+
|
|
102
|
+
## Security
|
|
103
|
+
|
|
104
|
+
SQL2API runs whatever SQL it is given against your databases, so it ships locked down and expects you to finish the job:
|
|
105
|
+
|
|
106
|
+
- Set `SQL2API_API_KEY` and serve over TLS (put it behind a reverse proxy).
|
|
107
|
+
- Connect with a database account that only has the privileges the API needs - the read-only guard is
|
|
108
|
+
defence in depth, not a replacement for grants. (H2's driver cannot enforce read-only, so H2 relies on the guard.)
|
|
109
|
+
- Use bound `:name` parameters. The older `{name}` placeholders paste text into the SQL and are therefore restricted
|
|
110
|
+
to numbers and plain text.
|
|
111
|
+
- Saved-query files are only read from `saved_sql/`; passwords are never returned by the API.
|
|
112
|
+
|
|
113
|
+
See [SECURITY.md](SECURITY.md) to report a vulnerability.
|
|
114
|
+
|
|
115
|
+
## Docker
|
|
116
|
+
|
|
117
|
+
~~~bash
|
|
118
|
+
docker build -t sql2api . # add --build-arg WITH_H2=true for H2 support
|
|
119
|
+
docker run -p 5000:5000 -v "$PWD/data:/data" -e SQL2API_API_KEY=change-me sql2api
|
|
120
|
+
~~~
|
|
121
|
+
|
|
122
|
+
The container keeps `db_connections.json` and `saved_sql/` in `/data`. It runs gunicorn with a single worker
|
|
123
|
+
(the files are protected by an in-process lock).
|
|
124
|
+
|
|
125
|
+
## API overview
|
|
126
|
+
|
|
127
|
+
| Endpoint | Method | Purpose |
|
|
128
|
+
|----------|--------|---------|
|
|
129
|
+
| `/execute_sql` | POST | Run ad-hoc SQL (`sql`, `connection_name`, optional `params`). |
|
|
130
|
+
| `/q/<name>` | GET, POST | Run a saved query; query-string or body values become parameters. |
|
|
131
|
+
| `/save_sql_to_file` | PATCH | Save a query (creates the next version). |
|
|
132
|
+
| `/list_files` | GET | List saved queries and their versions (`sort_by`, `sort_order`). |
|
|
133
|
+
| `/saved_sql/<name>` | DELETE | Delete a saved query or one `?version=`. |
|
|
134
|
+
| `/view_file_content` | GET | Raw content of a saved query file. |
|
|
135
|
+
| `/execute_sql_from_file`, `/execute_sql_with_parameters_from_file` | POST | Run a saved query by `filepath` (same as `/q/<name>`). |
|
|
136
|
+
| `/connections` | GET, PATCH | List (passwords masked) / add / update connections. |
|
|
137
|
+
| `/connections/<name>` | DELETE | Remove a connection. |
|
|
138
|
+
| `/health`, `/docs`, `/openapi.json` | GET | Liveness, Swagger UI, OpenAPI spec. |
|
|
139
|
+
|
|
140
|
+
Full details are in [documentation/API.md](documentation/API.md).
|
|
141
|
+
|
|
142
|
+
## Development
|
|
143
|
+
|
|
144
|
+
~~~bash
|
|
145
|
+
pip install -e ".[dev]"
|
|
146
|
+
ruff check .
|
|
147
|
+
python -m unittest discover -s tests -t .
|
|
148
|
+
~~~
|
|
149
|
+
|
|
150
|
+
The integration tests in `tests/test_integration.py` run against real MySQL, PostgreSQL, ClickHouse and H2 servers when
|
|
151
|
+
the matching `SQL2API_IT_*` variables are set, and are skipped otherwise; CI runs them against service containers.
|
|
152
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the pull request process, and [CHANGELOG.md](CHANGELOG.md) for what changed.
|
|
153
|
+
|
|
154
|
+
## Third-party components
|
|
155
|
+
|
|
156
|
+
The wheel bundles the [H2 Database](https://h2database.com) JDBC driver (MPL 2.0 / EPL 1.0). The sample SQLite
|
|
157
|
+
databases in `examples/` derive from the Sakila and Chinook sample datasets.
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
[MIT](LICENSE) © Anantha Raju C
|
|
162
|
+
|
|
163
|
+
## Contact
|
|
164
|
+
|
|
165
|
+
Anantha Raju C - [@anantharajuc](https://twitter.com/anantharajuc) - arcswdev@gmail.com
|
|
166
|
+
|
|
167
|
+
Project link: <https://github.com/AnanthaRajuC/SQL2API>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sql2api"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Turn SQL into a REST API: run queries against MySQL, PostgreSQL, ClickHouse, SQLite or H2 over HTTP and get JSON, CSV, XML, YAML or XLSX back."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Anantha Raju C", email = "arcswdev@gmail.com" }]
|
|
13
|
+
keywords = ["sql", "api", "rest", "flask", "database", "mysql", "postgresql", "clickhouse", "sqlite", "h2"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Framework :: Flask",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Database",
|
|
21
|
+
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
|
22
|
+
]
|
|
23
|
+
dependencies = ["Flask>=2.2", "PyYAML>=6.0", "openpyxl>=3.1"]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
mysql = ["mysql-connector-python>=8.0"]
|
|
27
|
+
postgres = ["psycopg2-binary>=2.9"]
|
|
28
|
+
clickhouse = ["clickhouse-driver>=0.2"]
|
|
29
|
+
h2 = ["JayDeBeApi>=1.2", "JPype1>=1.4"] # also needs a Java runtime
|
|
30
|
+
all = ["sql2api[mysql,postgres,clickhouse,h2]"]
|
|
31
|
+
server = ["gunicorn>=21"]
|
|
32
|
+
dev = ["sql2api[all]", "ruff>=0.5"]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/AnanthaRajuC/SQL2API"
|
|
36
|
+
Issues = "https://github.com/AnanthaRajuC/SQL2API/issues"
|
|
37
|
+
Changelog = "https://github.com/AnanthaRajuC/SQL2API/blob/main/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
sql2api = "sql2api.cli:main"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.dynamic]
|
|
43
|
+
version = { attr = "sql2api.__version__" }
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
include = ["sql2api*"]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
sql2api = ["lib/*.jar"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
target-version = "py39"
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
select = ["E", "F", "W", "I", "B"]
|
sql2api-0.1.0/setup.cfg
ADDED