https://github.com/c0m371/restsql
RestSQL: A lightweight .NET tool that turns SQL queries (defined in YAML) into ready-to-run REST endpoints. Works standalone or as a library, supports transactions, nested JSON output, and multiple database providers.
https://github.com/c0m371/restsql
asp-net-core csharp database dotnet generation mysql oracle postgresql rest-api sql sqlite sqlserver yaml
Last synced: 5 months ago
JSON representation
RestSQL: A lightweight .NET tool that turns SQL queries (defined in YAML) into ready-to-run REST endpoints. Works standalone or as a library, supports transactions, nested JSON output, and multiple database providers.
- Host: GitHub
- URL: https://github.com/c0m371/restsql
- Owner: c0m371
- License: agpl-3.0
- Created: 2025-09-27T16:51:41.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-11-07T18:16:46.000Z (8 months ago)
- Last Synced: 2025-11-07T19:14:48.512Z (8 months ago)
- Topics: asp-net-core, csharp, database, dotnet, generation, mysql, oracle, postgresql, rest-api, sql, sqlite, sqlserver, yaml
- Language: C#
- Homepage:
- Size: 86.9 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# RestSQL
| | |
|--------|:--------------------------------------------------------------------------------------------------------:|
| Nuget | [](https://www.nuget.org/packages/Comet1.RestSQL) |
| Docker | [](https://hub.docker.com/r/cometone/restsql) |
RestSQL is a lightweight **.NET tool** that turns SQL queries (defined in **YAML**) into ready-to-run **REST endpoints**. Works standalone or as a library, supports transactions, nested JSON output, multiple database providers, and **OAuth 2.0/OpenID Connect (OIDC) authentication**.
## Getting Started
There are two ways to use RestSQL:
### 1. Standalone API (Using RestSQL.Api)
#### Build from source
The simplest way to get started is using the pre-built API project:
1. Clone the repository
2. Configure your `appsettings.json`:
```json
{
"RestSQL": {
"ConfigFolder": "path/to/your/yaml/configs"
},
"Serilog": {
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "Console"
}
]
}
}
```
3. Run the project:
```sh
cd src/RestSQL.Api
dotnet run
```
#### Alternatively, use docker
[](https://hub.docker.com/r/cometone/restsql)
Set the `RestSQL:ConfigFolder` key using the environment variable name `RestSQL__ConfigFolder` (double underscore maps to a colon in ASP.NET configuration).
Example Docker run (mount local configs and set env):
```sh
docker run --rm -p 7017:8080 \
-v $(pwd)/my-yaml-configs:/app/config \
-e RestSQL__ConfigFolder=/app/config \
cometone/restsql:latest
```
Running inside containers: connection string tips
When your API runs inside Docker, the database host in connection strings must be reachable from the container. A few common patterns:
- Docker Desktop (Windows/macOS): use `host.docker.internal` to reach services running on the host machine. Example Postgres connection string:
`Host=host.docker.internal;Port=5432;Database=mydb;Username=user;Password=pass`
- Linux Docker (host.docker.internal): older Docker engines on Linux don't provide `host.docker.internal` by default. You can add it at container startup with `--add-host` (Docker 20.10+ with host-gateway):
```sh
docker run --add-host=host.docker.internal:host-gateway \
-e RestSQL__ConfigFolder=/app/config \
-v $(pwd)/my-yaml-configs:/app/config \
cometone/restsql:latest
```
### 2. Library Usage (Adding to Existing Project)
Add RestSQL to your ASP.NET Core project:
```csharp
// Program.cs
builder.Services.AddRestSQL(builder.Configuration);
// Configure middleware
app.UseRestSQL(builder.Configuration);
```
Either link the project via the source code, or use Nuget

## Configuration
RestSQL uses YAML files for configuration. You need two main configuration sections:
1. connections - Database connection definitions
2. endpoints - Endpoint definitions
You can split these across files as you see fit, they will be merged.
A single file is also fine.
### Database Connections
Define your database connections under connections:
```yaml
connections:
postgres1:
type: PostgreSQL
connectionString: "Host=localhost;Database=mydb;Username=user;Password=pass"
mysql1:
type: MySql
connectionString: "Server=localhost;Database=mydb;User=user;Password=pass"
oracle1:
type: Oracle
connectionString: "Data Source=localhost:1521/XEPDB1;User Id=system;Password=pass"
sqlserver1:
type: SqlServer
connectionString: "Server=localhost;Database=mydb;User=sa;Password=pass;TrustServerCertificate=True"
sqlite1:
type: Sqlite
connectionString: "Data Source=local.db"
```
### Authentication Configuration
For secured endpoints, define your OIDC/OAuth 2.0 configuration globally under `authentication`. This section is **optional**.
```yaml
authentication:
authority: https://auth.example.com/
audience: https://api.example.com
scopes:
- write:posts
```
### Endpoint Configuration
Define REST endpoints. Endpoints can include optional authorization settings.
See section "Example blog API" below.
## Example Blog API
Let's look at a complete blog post API example:
```yaml
connections:
blog:
type: PostgreSQL
connectionString: "Host=localhost;Database=restsql_blog;Username=restsql_blog;Password=restsql_blog"
endpoints:
# Get all posts with tags
- path: /api/posts
method: GET
statusCode: 200
sqlQueries:
posts:
connectionName: blog
sql: >
select id post_id, title, description, creation_date, username
from posts;
tags: &tagsQuery
connectionName: blog
sql: >
select *
from tags;
outputStructure:
type: Object
isArray: true
queryName: posts
fields: &postFields
- { type: Long, name: id, columnName: post_id }
- { type: String, name: title, columnName: title }
- { type: String, name: description, columnName: description }
- { type: String, name: username, columnName: username }
- { type: String, name: creationDate, columnName: creation_date }
- type: string
isArray: true
name: tags
queryName: tags
columnName: tag
linkColumn: post_id
# Get specific post
- path: /api/posts/{id}
method: GET
statusCode: 200
statusCodeOnEmptyResult: 404
sqlQueries:
posts:
connectionName: blog
sql: >
select id post_id, title, description, creation_date, username
from posts
where id = :id::int;
tags: &tagsQuery
connectionName: blog
sql: >
select *
from tags;
outputStructure:
type: Object
isArray: true
queryName: posts
fields: *postFields
# Create new post, and return the created post
# Requires authorization
- path: /api/posts
method: POST
statusCode: 200
authorize: true # Requires a valid JWT bearer token
authorizationScope: write:posts # Token must contain this scope claim
writeOperations:
- connectionName: blog
sql: >
insert into posts (title, description, creation_date, username)
values (:title, :description, current_timestamp, :username)
returning id;
bodyType: Object
outputCaptures:
- columnName: id
parameterName: post_id
sqlQueries:
posts:
connectionName: blog
sql: >
select id post_id, title, description, creation_date, username
from posts
where id = @post_id;
tags: *tagsQuery
outputStructure:
type: Object
queryName: posts
fields: *postFields
```
### Making Requests
Get posts:
```sh
GET /api/posts
Response:
[
{
"id": 1,
"title": "The Joys of Async/Await",
"description": "A deep dive into non-blocking operations in C#.",
"creationDate": "2025-10-25T15:03:04",
"username": "alice_codes",
"tags": ["C#", "Async"]
}
]
```
Create post (with Authorization):
You must include an `Authorization: Bearer ` header where the token grants the `write:posts` scope.
```sh
POST /api/posts
Authorization: Bearer
{
"title": "PostgreSQL vs MySQL",
"description": "A performance comparison",
"username": "bob_devs"
}
Response:
{
"id": 2,
"title": "PostgreSQL vs MySQL",
"description": "A performance comparison",
"username": "bob_devs",
"creationDate": "06/11/2025 10:30:41",
"tags": []
}
```
Get specific post:
```sh
GET /api/posts/2
Response:
[
{
"id": 2,
"title": "PostgreSQL vs MySQL: A Performance Review",
"description": "Comparing the speed and features of two popular databases.",
"username": "alice_codes",
"creationDate": "22/10/2025 22:00:00",
"tags": [
"PostgreSQL",
"Database"
]
}
]
```
## Advanced Features
### Output Structure Transformation
You can define complex nested JSON structures.
Queries can be linked through a linkColumn.
```yaml
sqlQueries:
posts:
connectionName: blog
sql: >
select id post_id, title, description, creation_date, username
from posts;
tags:
connectionName: blog
sql: >
select *
from tags;
outputStructure:
type: Object
isArray: true
queryName: posts
fields:
- { type: Long, name: id, columnName: post_id }
- { type: String, name: title, columnName: title }
- { type: String, name: description, columnName: description }
- { type: String, name: username, columnName: username }
- { type: String, name: creationDate, columnName: creation_date }
- type: string
isArray: true
name: tags
queryName: tags
columnName: tag
linkColumn: post_id
```
### Parameter Capture
Capture output from write operations:
```yaml
writeOperations:
- connectionName: postgres1
sql: "INSERT INTO posts ... RETURNING id"
outputCaptures:
- columnName: id
parameterName: postId
- connectionName: postgres1
sql: "INSERT INTO tags (post_id, tag) VALUES (@postId, @tag)"
```
### Transaction Support
Multiple write operations are automatically wrapped in a transaction:
```yaml
writeOperations:
- connectionName: postgres1
sql: "INSERT INTO posts ..."
- connectionName: postgres1
sql: "INSERT INTO tags ..." # Rolled back if posts insert fails
```
### Parameter Binding
- Route parameters: `{id}` in path
- Query parameters: `?search=term`
- Request body: JSON object or value
## Development Setup
1. Clone the repository
2. Install .NET 9.0 SDK
3. Run tests:
```sh
dotnet test
```
Note that docker should be running to be able to run the integration tests with test containers.
4. Start the API:
```sh
cd src/RestSQL.Api
dotnet run
```
For more examples, check out the integration tests in the `tests/RestSQL.IntegrationTests` directory.
## Supported Databases
- PostgreSQL
- SQL Server
- MySQL
- Oracle
- SQLite