REST API server that abstracts the need to write CRUD methods by exposing a standardized API to interact with a Postgres database

Overview

Basiliq

Exposing a Postgres database via a REST API that follows the JSON:API specs.
All in all, a tasty API.

What is Basiliq

Basiliq is a very alpha REST API that replaces the need to write CRUD methods by exposing a standardized API to interact with a Postgres database

It follows the established JSON:API specifications. Written in Rust, it tries to conciliate performance with stability.

Quickstart

Ready to use example

You could try out the API already deployed on Heroku.

For instance:

# For a very simple example
curl 'http://demo.basiliq.io/public__peoples'

# For a more complex example
curl 'http://demo.basiliq.io/public__peoples?include=public__articles,public__comments&fields\[public__comments\]='

Running locally

One can install basiliq through docker. An example docker-compose script is available at the root of the repository. To start it:

# To start
docker-compose -f docker-compose.example.yml up -d

# At that point the database is empty,
# you can populate it with the following script
curl -L https://gitlab.com/basiliqio/basiliq_db_test_utils/-/jobs/artifacts/main/raw/basiliq_test.dump\?job\=pack_test_migrations | PGHOST=localhost PGUSER=postgres PGPASS=postgres psql

# Then you can restart the basiliq server so it rescans the database
docker-compose -f docker-compose.example.yml restart basiliq

# To stop
docker-compose -f docker-compose.example.yml down

Understanding the API

How to query

In the future, there should be a way to generate an OpenApi document to view exactly how the API is accessible.

The API response follows the JSON:API specifications.

By default, the endpoints are exposed in the format schema__table (i.e. for a table peoples in the public schema, the endpoint would be public__table).

By modifying the configuration one could change how those endpoints are exposed.

Example requests

Creating request

Notice the lack of id in the request.

Also, in the response, the fields that were not specified are set to their default

POST /public__peoples HTTP/1.1
Host: demo.basiliq.io
User-Agent: curl/7.76.1
Content-Type:application/vnd.api+json
Accept: application/json, */*
Content-Length: 174

{
    "data": {
        "type": "public__peoples",
        "attributes": {
            "first-name": "Somebody",
            "last-name": "Once_told_me_the_world",
            "gender": "F",
            "twitter": "@allstars"
        }
    }
}

HTTP/1.1 201 Created
Connection: keep-alive
Content-Type: application/vnd.api+json
Content-Length: 224
Date: Sun, 02 May 2021 20:20:52 GMT

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": {
        "type": "public__peoples",
        "id": "d14e1928-9cae-441c-945d-144ebe6c94c8",
        "attributes": {
            "age": null,
            "first-name": "Somebody",
            "gender": "F",
            "last-name": "Once_told_me_the_world",
            "twitter": "@allstars"
        }
    }
}
Simple fetching request
GET /public__peoples HTTP/1.1
Host: demo.basiliq.io
User-Agent: curl/7.76.1
Accept: application/json, */*

HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/vnd.api+json
Content-Length: 598
Date: Sun, 02 May 2021 20:13:47 GMT

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": [
        {
            "type": "public__peoples",
            "id": "1649b1e9-8a5f-4f52-b331-c07ce3bccc6f",
            "attributes": {
                "age": 22,
                "first-name": "Francis",
                "gender": "M",
                "last-name": "Le Roy",
                "twitter": null
            }
        },
        {
            "type": "public__peoples",
            "id": "777cc565-c66b-4942-ab44-8fc5f194b804",
            "attributes": {
                "age": 34,
                "first-name": "Somebody",
                "gender": "F",
                "last-name": "Wuhu",
                "twitter": "@randomhandle"
            }
        },
        {
            "type": "public__peoples",
            "id": "961e543a-4b22-4d48-a8e5-c1eafada950f",
            "attributes": {
                "age": null,
                "first-name": "AAAAAAAA",
                "gender": null,
                "last-name": "BBBBBBBBB",
                "twitter": null
            }
        }
    ]
}
Fetching requests including relationships and sparsing fields

You can find the attributes of the objects in the relationships key of each main resource in the included key below.

Notice that the comments object have only ids, because none of their fields was selected via the fields[public__comments]= query parameter.

GET /public__peoples?include=public__articles,public__comments&fields[public__comments]= HTTP/1.1
Host: demo.basiliq.io:80
User-Agent: curl/7.76.1
Accept: application/json, */*

HTTP/1.1 200 OK
content-type: application/vnd.api+json
content-length: 1879
date: Sun, 02 May 2021 20:08:12 GMT

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": [
        {
            "type": "public__peoples",
            "id": "1649b1e9-8a5f-4f52-b331-c07ce3bccc6f",
            "attributes": {
                "age": 22,
                "first-name": "Francis",
                "gender": "M",
                "last-name": "Le Roy",
                "twitter": null
            },
            "relationships": {
                "public__articles": {
                    "data": [
                        {
                            "type": "public__articles",
                            "id": "fdf715dd-8772-498c-8196-6f4ccb64edef"
                        },
                        {
                            "type": "public__articles",
                            "id": "2dbf5d1a-b029-4456-af6b-339c75b1089c"
                        }
                    ]
                },
                "public__comments": {
                    "data": [
                        {
                            "type": "public__comments",
                            "id": "59f58abd-c9db-4074-9c34-ac33e9c838ce"
                        },
                        {
                            "type": "public__comments",
                            "id": "c2add83b-6f58-45a2-bf62-3ebc05c46192"
                        }
                    ]
                }
            }
        },
        {
            "type": "public__peoples",
            "id": "777cc565-c66b-4942-ab44-8fc5f194b804",
            "attributes": {
                "age": 34,
                "first-name": "Somebody",
                "gender": "F",
                "last-name": "Wuhu",
                "twitter": "@randomhandle"
            },
            "relationships": {
                "public__articles": {
                    "data": {
                        "type": "public__articles",
                        "id": "46c4fe50-8c56-4f26-935e-56ccfa496bb5"
                    }
                },
                "public__comments": {
                    "data": {
                        "type": "public__comments",
                        "id": "6ae9938f-d490-4707-b138-770c2a52465f"
                    }
                }
            }
        },
        {
            "type": "public__peoples",
            "id": "961e543a-4b22-4d48-a8e5-c1eafada950f",
            "attributes": {
                "age": null,
                "first-name": "AAAAAAAA",
                "gender": null,
                "last-name": "BBBBBBBBB",
                "twitter": null
            }
        }
    ],
    "included": [
        {
            "type": "public__articles",
            "id": "2dbf5d1a-b029-4456-af6b-339c75b1089c",
            "attributes": {
                "body": "Yeah I know ! Right ?!",
                "title": "Oh my g**"
            }
        },
        {
            "type": "public__articles",
            "id": "46c4fe50-8c56-4f26-935e-56ccfa496bb5",
            "attributes": {
                "body": "They feast on the blood of the departed draw their powers",
                "title": "Why devs require sacrifices"
            }
        },
        {
            "type": "public__articles",
            "id": "fdf715dd-8772-498c-8196-6f4ccb64edef",
            "attributes": {
                "body": "Yes",
                "title": "How to dead"
            }
        },
        {
            "type": "public__comments",
            "id": "59f58abd-c9db-4074-9c34-ac33e9c838ce"
        },
        {
            "type": "public__comments",
            "id": "6ae9938f-d490-4707-b138-770c2a52465f"
        },
        {
            "type": "public__comments",
            "id": "c2add83b-6f58-45a2-bf62-3ebc05c46192"
        }
    ]
}
Updating request

Notice that attributes that were not included in the PATCH request are not nulled.

PATCH /public__peoples/777cc565-c66b-4942-ab44-8fc5f194b804 HTTP/1.1
Host: demo.basiliq.io
User-Agent: curl/7.76.1
Content-Type:application/vnd.api+json
Accept: application/json, */*
Content-Length: 204

{
    "data": {
        "type": "public__peoples",
        "id": "777cc565-c66b-4942-ab44-8fc5f194b804",
        "attributes": {
            "first-name": "NotTheOriginalFirstName",
            "last-name": "NotTheOriginalLastName"
        }
    }
}

HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/vnd.api+json
Content-Length: 260
Date: Sun, 02 May 2021 20:24:50 GMT

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": {
        "type": "public__peoples",
        "id": "777cc565-c66b-4942-ab44-8fc5f194b804",
        "attributes": {
            "age": 34,
            "first-name": "NotTheOriginalFirstName",
            "gender": "F",
            "last-name": "NotTheOriginalLastName",
            "twitter": "@randomhandle"
        }
    }
}
Deleting request
DELETE /public__peoples/777cc565-c66b-4942-ab44-8fc5f194b804 HTTP/1.1
Host: demo.basiliq.io
User-Agent: curl/7.76.1
Accept: application/json, */*

HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/vnd.api+json
Content-Length: 41
Date: Sun, 02 May 2021 20:25:54 GMT

{
    "jsonapi": {
        "version": "1.0"
    },
    "data": null
}

The configuration

Generating

Typically, one would first need to create a configuration, however this is not mandatory to run basiliq.

To create a configuration, one can use :

basiliq config generate

It would generate a file called basiliq_config.yaml in the current working directory.

What's in there

This file would look like the following :

---
resources:                 # The list of resources
  public__articles:        # The name of a resource. *It can be changed*
    target:                # The identifier object of the resource
      schema: public       # The schema this resource is bound to in the database
      table: articles      # The name of the table bound to this resource
    enabled: true          # `true` if this resource is enabled
    relationships:         # A list of relationships
      public__comments:    # Name of the relationship. *It can be changed*
        target:            # The identifier object of the resource
          schema: public   # The schema this relationship's resource is bound to in the database
          table: comments  # The name of the table bound to this relationship's resource
        enabled: true      # `true` if this relationship is enabled
        field: article     # The field to which this relationship is bound
      public__peoples:
        target:
          schema: public
          table: peoples
        through:           # Identifies the bucket table for Many-to-Many relationships 
          schema: public   # The schema of the bucket resource
          table: comments  # The table of the bucket resource
          field: author    # The field linking the relationship's resource and the bucket resource
        enabled: true
        field: id
[...]

Checking the configuration

After having generated the configuration, one might need to ensure it's correct after having modified it.

One could do that with the following command:

basiliq config check --input basiliq_config.yaml 

Testing

To test this crate, you need to start a Postgres database and export the DATABASE_URL environment variable.

You can use the provided docker-compose plan

# To start the test database
docker-compose -f docker-compose.testing.yml up -d

# Don't forget to set the environment variable
export DATABASE_URL="postgres://postgres:postgres@localhost/postgres"

# Run the tests
cargo test

# To stop the test database
docker-compose -f docker-compose.testing.yml down -v
Comments
  • Plugin system

    Plugin system

    To allow for more modularity, a plugin system would be most welcome.

    But multiple questions are to be asked before diving into the code:

    • [ ] How the plugins will interact the main process (Shared library, WASM module, sub-process, etc..)
    • [ ] How many plugin levels should there be (pre-requests, post-requests, pre-db-scanning, etc...)
    • [ ] How to ensure those plugins can be trusted (digital signature like PGP ?)
    • [ ] What are the performance problems induced by loading externals resources rather than developing those features in the main project.
    enhancement help wanted question 
    opened by GrandChaman 0
  • Sign configuration

    Sign configuration

    The configuration of Basiliq is very precious. If somebody were to tinker with it, it could have disastrous consequences for the service.

    There should be an option to verify a signature along with the configuration to ensure that the configuration has only been tampered with by trusted members.

    • [ ] Verify detached PGP signatures
    • [ ] Import public keys from local storage (dangerous?)
    • [ ] Import public keys from the web (dangerous?)
    enhancement help wanted 
    opened by GrandChaman 0
  • Configure which database schemas to scan

    Configure which database schemas to scan

    There should be an option to choose from which database schema is scanned.

    • [ ] Choose a single database schema to scan
    • [ ] Choose multiple database schemas to scan
    enhancement good first issue 
    opened by GrandChaman 0
  • Use scapegoat tree

    Use scapegoat tree

    In many places of this project, there's heavy use of the BTreeMap from rust collection.

    The scapegoat crate could help with performance by using stack allocated array for small maps.

    The API of the crates looks very similar to BTreeMap's one, it shouldn't be too difficult to make the switch.

    enhancement good first issue 
    opened by GrandChaman 0
  • Better support for Postgres errors

    Better support for Postgres errors

    There should be a better support for Postgres errors. It should differentiate between multiple errors types.

    • [ ] Detect duplicate error
    • [ ] Detect CHECK constraint error
    • [ ] Detect uniqueness error
    • [ ] Detect missing field error
    enhancement 
    opened by GrandChaman 0
  • Disable resources

    Disable resources

    There should be an option to disable a resource.

    TODOs:

    • [ ] Option to prevent a resource from beeing accessed, as a relationship, or a as a main resource
    • [ ] Option to allow a disabled resource to be a "bucket" for "Many-to-Many" relationships, but not beeing accessible directly.
    enhancement 
    opened by GrandChaman 0
Owner
Basiliq
Basiliq
Grape is a REST-like API framework for Ruby

Grape is a REST-like API framework for Ruby. It's designed to run on Rack or complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily develop RESTful APIs. It has built-in support for common conventions, including multiple formats, subdomain/prefix restriction, content negotiation, versioning and much more.

Ruby Grape 9.7k Jan 2, 2023
Rust Rest API Stack with User Management

A secure-by-default rest api stack implemented with hyper, tokio, bb8 and postgres. This project is focused on providing end-to-end encryption by default for 12-factor applications. Includes a working user management and authentication backend written in postgresql with async S3 uploading for POST-ed data files.

Jay 10 Dec 25, 2022
A customizable, simple and easy to use json REST API consumer

JACK is a generic JSON API client. It is useful to interact with APIs from multiple services such as Google and Twitter

Mente Binária 6 May 22, 2022
Print Apple WeatherKit REST API weather conditions and hourly/daily foreacast to the console.

weatherkit-rust A Rust CLI program to print current conditions and daily/hourly forecast to the console. Please read authorization.md as you need an A

boB Rudis 11 Dec 23, 2022
Rust Rocket MongoDB token-authorization REST API boilerplate

Rust Rocket MongoDB token-auth REST API boilerplate In this repository, you can find backend Rust rocket mongodb rest-api boilerplate with token autho

null 6 Dec 7, 2022
A newsletter with actix-web and sqlx-postgres

Newsletter backend Health check: production Pre-requisites You'll need to install: Rust Docker There are also some OS-specific requirements. Windows c

Nadeem Bhati 4 Dec 10, 2022
Quick demo of a REST frontend with a Redis session store.

axum-rest-starter-example Important Tasks Ensure session UUID is unique Protect /api/ with JWT Add CSRF CORS? Dev Setup (1) Run docker compose up to f

Michael de Silva 23 Dec 31, 2022
Write Rack middleware in Rust.

RacksOnRacks Write Rack middleware in Rust. This repo is a proof of concept and should be used as an example. The best way to use this at the moment w

Odin 5 Jul 11, 2023
A Rust Boilerplate server with GraphQL API, Diesel, PostgreSQL, session authentication and JWT

Canduma rust Graphql A Rust authentication server with GraphQL API, Diesel, PostgreSQL session authentication and JWT This repository contains a Graph

Julien Lenne 738 Dec 28, 2022
A simple authentication flow using Rust and Actix-web, with a PostgreSQL database and a sveltekit frontend.

Rust-auth-example This repository aims to represent a simple authentication flow using Rust and Actix-web, with a PostgreSQL database and a sveltekit

Kival Mahadew 4 Feb 19, 2023
SquareDB - A new database

SquareDB The idea behind this was to create a new database, focusing on the data structures and algorithms behind it. I am also really interested in l

Goren Barak 7 Oct 20, 2023
A secure and efficient gateway for interacting with OpenAI's API, featuring load balancing, user request handling without individual API keys, and global access control.

OpenAI Hub OpenAI Hub is a comprehensive and robust tool designed to streamline and enhance your interaction with OpenAI's API. It features an innovat

Akase Cho 30 Jun 16, 2023
Markdown LSP server for easy note-taking with cross-references and diagnostics.

Zeta Note is a language server that helps you write and manage notes. The primary focus is to support Zettelkasten-like1, 2 note taking by providing an easy way to cross-reference notes (see more about features below).

Artem Pyanykh 4 Oct 27, 2022
A super-easy, composable, web server framework for warp speeds.

warp A super-easy, composable, web server framework for warp speeds. The fundamental building block of warp is the Filter: they can be combined and co

Sean McArthur 7.5k Jan 2, 2023
Hot reload static web server for deploying mutiple static web site with version control.

SPA-SERVER It is to provide a static web http server with cache and hot reload. 中文 README Feature Built with Hyper and Warp, fast and small! SSL with

null 7 Dec 18, 2022
Proxies all incoming connections to a minecraft server of your choosing, while also logging all ping and login requests to a json file and discord webhook.

minecraft-honeypot Proxies all incoming connections to a minecraft server of your choosing, while also logging all ping and login requests to a json f

Cleo 19 Jan 4, 2023
An extensible music server written in Rust 🚀🎵✨

Music Player (written in Rust) Note: This is a work in progress. This is a simple music player that I made for my own use. It is written in Rust and u

Tsiry Sandratraina 62 Jan 7, 2023
An adapter to easily allow an Axum server to be run within a Cloudflare worker.

axum-cloudflare-adapter An adapter to easily allow an Axum server to be run within a Cloudflare worker. Usage use worker::*; use axum::{ response

Logan Keenan 4 Feb 28, 2023
Rust GraphQL server using simple type-only schema

SimpleGQL This library allows a simplified GraphQL schema to be given and will run a server with a backend store (currently only SQLite) and a set of

Daniel Cocks 5 May 10, 2023