Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Datasources

ci

Datasources is a data-access and object-relational mapper library. It aims for parity across Languages and Datasource Providers.

The Feature Stack: starts with commands, connections, and object mapping. Higher up, the API is more opinionated. Types and repositories are generated from a Types Specification, which can be the source of truth for the datasource schema and the generated code. The specification describes unions and inherited types. A type may contain referenced lists and dictionaries, and those references are generated as enriched repositories.

Thus, due to the well-defined Feature stack, the library can be used as a high-level repository builder or as a micro-ORM, alike.

The specification is declarative, so it suits generation by AI agents. Progressive skills keep that generation economical and repeatable. They use fewer tokens, and they keep generated code and mappings constant and deterministic. The library was written so TypeScript could be translated to Rust, and so data-access code written by an agent would remain coherent as an application scales.

Quick Start

Installing the skill

npx skills add deterministic-code/datasources

Installation from source

TypeScript
npm install @deterministic-code/datasources
C#
dotnet add package Datasources
Rust
cargo add datasources

Setup

Prompt

Run setup and add sqlite and postgres for csharp and rust

The skill runs the CLI. setup writes .env and installs the requested datasources for each language.

npx @deterministic-code/datasources-cli setup --yes --languages csharp rust --datasources sqlite postgres

Run interactive setup when you want prompts instead of --yes.

npx @deterministic-code/datasources-cli setup

Generating code from specification

Prompt

Generate all types, repositories, and SQL for csharp, rust, and typescript

A Types Specification is the deterministic directory. types.yaml lists each entity. Tag a type datasource_type when it is a table.

version: 1.0.0
types:
  - item:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - name:
            type: string
            size: 128

generate all writes the types, the repositories, and SQL for one language. It also writes SQL migrations and runs migrate-up. The default input directory is ./deterministic. The default output directory is ./generated. See Migrating.

C#
npx @deterministic-code/datasources-cli generate all --dialect csharp
Rust
npx @deterministic-code/datasources-cli generate all --dialect rust
TypeScript
npx @deterministic-code/datasources-cli generate all --dialect typescript

See Generate. Walkthroughs for a User, an enriched users-and-roles spec, and a legacy table mapping are on Repository Generation.

Migrating

Prompt

Create migrations and migrate up for sqlite

generate all writes SQL migrations under sql/<dialect>/migrations and runs migrate-up. The connection is PROVIDER and DATASOURCE_URL from .env. Process env overrides the file. An in-memory DATASOURCE_URL skips migrate-up.

PROVIDER=sqlite
DATASOURCE_URL=sqlite:///./app.sqlite
npx @deterministic-code/datasources-cli generate all --dialect typescript

--no-migrations skips writing the migration files. --no-auto-migrate-up skips migrate-up. --dry-run prints the migrate-up command and leaves the migration files unwritten.

npx @deterministic-code/datasources-cli generate all --dialect typescript --no-migrations
npx @deterministic-code/datasources-cli generate all --dialect typescript --no-auto-migrate-up

See Migrations.

Adding migrations manually

migrations create writes the same SQL files as generate all and leaves the database unchanged. migrate-up applies every pending file. migrate-down rolls one step back and needs the token it prints, passed as --confirm.

npx @deterministic-code/datasources-cli migrations create
npx --yes --package @deterministic-code/migraters migrate-up --provider sqlite --connection ./app.sqlite
npx --yes --package @deterministic-code/migraters migrate-down --provider sqlite --connection ./app.sqlite --confirm TOKEN

Add a migration that generate keeps. Put an up file and a down file under deterministic/custom/<dialect>/. The file name is <order>_<name>_<up|down>.sql. order is 1–999. name starts with a letter or digit. The next generate all copies the pair as <order>_up_custom_<order>_<name>_<up|down>.sql into sql/<dialect>/migrations and runs migrate-up. Order N runs after generated migration N (0001_initial, 000N_up, 000N_stored_procedures) and before N+1, so a seed can use 0001 when the schema is 0001_initial.

deterministic/custom/sqlite/0001_add_notes_up.sql
deterministic/custom/sqlite/0001_add_notes_down.sql

Scaffold an empty pair in the migrations directory with migraters:

npx --yes --package @deterministic-code/migraters migrate-create --provider sqlite --name add_notes

Using the generated code

setup and generate wire the generated registry so Datasource.Default / Datasource.default / default() binds Items / items / items(). Create / create / create returns that entity. ReadSingle / readSingle / read_single returns the same entity.

C#
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

Item created = await ds.Items.Create(new Item { Name = "Ada" });
Item? loaded = await ds.Items.ReadSingle();
created.Name = "Ada Lovelace";
Response updated = await ds.Items.Update(created);
Response deleted = await ds.Items.Delete(created);
Rust
use datasources::{default, Response, Row, Value};

let ds = default()?;
let items = ds.as_ref().items();
let mut created = Row::new();
created.insert("name".into(), Value::from("Ada"));
let mut created = items.create(&mut created).await?;
let loaded = items.read_single(None, None, None).await?;
created.insert("name".into(), Value::from("Ada Lovelace"));
let updated: Response = items.update(&created, None).await?;
let deleted: Response = items.delete(&created, None).await?;
TypeScript
import { Datasource } from "@deterministic-code/datasources";

const ds = Datasource.default;

const created = await ds.items.create({ id: 0, name: "Ada" });
const loaded = await ds.items.readSingle();
created.name = "Ada Lovelace";
const updated = await ds.items.update(created);
const deleted = await ds.items.delete(created);

See Datasource.

Running a simple read command

Start with createCommand and executeReader. The connection must be open.

C#
var ds = Datasource.Default;
var connection = await ds.CreateConnection();
await connection.Open();

var command = await ds.CreateCommand(connection);
var reader = await command.ExecuteReader(
    "SELECT id, name FROM item WHERE id = $id",
    [new Parameter("id", 1)]);
while (await reader.Next())
    Console.WriteLine(reader.Current);
Rust
use datasources::{default, Parameter};

let ds = default()?;
let connection = ds.create_connection().await?;
connection.open().await?;

let command = ds.create_command(Some(connection)).await?;
let reader = command
    .execute_reader("SELECT id, name FROM item WHERE id = $id", Some(&[Parameter::new("id", 1)]))
    .await?;
while reader.next().await? {
    println!("{:?}", reader.current());
}
TypeScript
import { Datasource } from "@deterministic-code/datasources";

const ds = Datasource.default;
const connection = await ds.createConnection();
await connection.open();

const command = await ds.createCommand(connection);
const reader = await command.executeReader("SELECT id, name FROM item WHERE id = $id", [
  { name: "id", value: 1 },
]);
while (await reader.next()) console.log(reader.current);

See Command for middleware and execute.

Create and using an Enriched Repository

A CRUDRepository fills related fields after read once enrichments are registered. When the related repository is also a CRUD repository, nested List children are written on create / update / delete. A readonly repository only fills.

C#
var contacts = new CRUDRepository<Contact>(connection);
contacts.AddEnriched(new("contact_type_name", "contact_type_id", "id", "name"), types);
contacts.AddEnriched(new("addresses", "id", "contact_id", Type: EnrichmentPropertyType.List), addresses);

var loaded = await contacts.ReadSingle(new Where(new Operator.Equals("id", 10)));
await contacts.Create(new Contact { first_name = "Ada", addresses = [new Address { line1 = "1 Main" }] });
Rust
let mut contacts = CRUDRepository::from_type::<Contact>(connection)?;
contacts
    .add_enriched(Enrichment::new("contact_type_name", "contact_type_id", "id", Some("name".into()), EnrichmentPropertyType::Object), types, None)
    .add_enriched(Enrichment::new("addresses", "id", "contact_id", None, EnrichmentPropertyType::List), addresses, None);

let loaded = contacts.read_single(Some(&Where::new(Operator::equals("id", 10))), None).await?;
contacts.create(contact).await?;
TypeScript
const contacts = new CRUDRepository(Contact, connection);
contacts.addEnriched({ destinationProperty: "contact_type_name", sourceProperty: "contact_type_id", relatedKey: "id", relatedProperty: "name" }, types);
contacts.addEnriched({ destinationProperty: "addresses", sourceProperty: "id", relatedKey: "contact_id", type: "list" }, addresses);

const loaded = await contacts.readSingle(new Where(new Operator.Equals("id", 10)));
await contacts.create({ first_name: "Ada", addresses: [{ line1: "1 Main" }] });

See Repositories for readonly, CRUD, and bulk.

Mappers

TransformMapper reshapes an already-converted object. Repository read is convert, then map.

C#
var mapper = new TransformMapper<Nested, Flat>(
    n => new Flat { UserId = n.User.Id, UserName = n.User.Name },
    f => new Nested { User = new User { Id = f.UserId, Name = f.UserName } });
var flat = mapper.Map(nested);
var back = mapper.ReverseMap(flat);
Rust
let mapper = TransformMapper::new(
    |n: Nested| Flat { user_id: n.user.id, user_name: n.user.name },
    |f: Flat| Nested { user: User { id: f.user_id, name: f.user_name } },
);
let flat = mapper.map(nested);
let back = mapper.reverse_map(flat);
TypeScript
const mapper = new TransformMapper<Nested, Flat>(
  (n) => ({ userId: n.user.id, userName: n.user.name }),
  (f) => ({ user: { id: f.userId, name: f.userName } }),
);
const flat = mapper.map(nested);
const back = mapper.reverseMap(flat);

See Mappers for TransformMapper, FlatToNestedMapper, and DictionaryMapper.

Type Converters

TypeConverter is only Convert / convert / convert and ReverseConvert / reverseConvert / reverse_convert. Attach field converters on MetaDataRowTypeConverter when the native provider cannot map the type.

C#
sealed class FlagConverter : TypeConverter<Value, Value>
{
    public override Value Convert(Value from) => from.ToObject() is long n && n == 1;
    public override Value ReverseConvert(Value to) => to;
}

var rows = new MetaDataRowTypeConverter<Item>(fields: new Dictionary<string, TypeConverter<Value, Value>>
{
    ["flag"] = new FlagConverter()
});
var item = rows.Convert(row);
Rust
struct FlagConverter;
impl TypeConverter<Value, Value> for FlagConverter {
    fn convert(&self, from: &Value) -> Result<Value> {
        Ok(match from {
            Value::Int(n) => Value::Bool(*n == 1),
            other => other.clone(),
        })
    }
    fn reverse_convert(&self, to: &Value) -> Result<Value> {
        Ok(to.clone())
    }
}

let rows = MetaDataRowTypeConverter::new(vec!["flag".into()]).with_fields(HashMap::from([(
    "flag".into(),
    Arc::new(FlagConverter) as Arc<dyn TypeConverter<Value, Value>>,
)]));
let item = rows.convert(&row)?;
TypeScript
class FlagConverter extends TypeConverter {
  convert = (value: Cell): Cell => value === 1;
  reverseConvert = (value: Cell): Cell => value;
}

const rows = new MetaDataRowTypeConverter({ flag: new FlagConverter() });
const item = rows.convert(row);

See Type Converters.

Samples

Contacts CLI copies a generated contacts app, then setup, generate, build, and run.

Using the skill

Run setup and add providers

Prompt

Run setup and add sqlite and postgres for csharp and rust

Expected

npx @deterministic-code/datasources-cli setup --yes --languages csharp rust --datasources sqlite postgres

Example prompts

Generate a user repository

Prompt

Generate a user with field id: username, is_enabled in types.yaml and generate repositories for csharp

Expected

version: 1.0.0
types:
  - user:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - username:
            type: string
            size: 128
        - is_enabled:
            type: boolean
npx @deterministic-code/datasources-cli generate all --dialect csharp
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

User created = await ds.Users.Create(new User { Username = "ada", IsEnabled = true });
User? loaded = await ds.Users.ReadSingle();
created.Username = "ada-v2";
Response updated = await ds.Users.Update(created);
Response deleted = await ds.Users.Delete(created);

Generate users and roles

Prompt

The same `user` table, plus `role` and a `user_role` junction tagged `many_to_many`, with a `roles` list on `user`. Generate TypeScript and create a user that has a role.

Expected

version: 1.0.0
types:
  - user:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - username:
            type: string
            size: 128
        - is_enabled:
            type: boolean
        - roles:
            type: role[]
            references: user_role.user_id
  - role:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - name:
            type: string
            size: 64
  - user_role:
      tags:
        - datasource_type
        - many_to_many
      fields:
        - user_id:
            references: user.id
        - role_id:
            references: role.id
npx @deterministic-code/datasources-cli generate all --dialect typescript
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

Role admin = await ds.Roles.Create(new Role { Name = "admin" });
User created = await ds.Users.Create(new User
{
    Username = "ada",
    IsEnabled = true,
    Roles = [admin],
});
User? loaded = await ds.Users.ReadSingle();
Response deleted = await ds.Users.Delete(created);

Run native SQL

Prompt

Insert an item and read it back with a command and SQL text. Do not use a repository.

Expected

sealed class Item
{
    public long id { get; set; }
    public string name { get; set; } = "";
}

var ds = Datasource.Default;
await using var connection = await ds.CreateConnection();
await connection.Open();
var command = connection.CreateCommand();
await command.Execute(
    Native.N("INSERT INTO items (id, name) VALUES ($id, $name)"),
    [new Parameter("id", 1), new Parameter("name", "a")]);
var converter = new MetaDataRowTypeConverter<Item>();
await using var createdReader = await command.ExecuteReader(
    Native.N("SELECT id, name FROM items WHERE id = $id"),
    [new Parameter("id", 1)]);
var created = await createdReader.Read().Select(row => converter.Convert(row)).ToListAsync();

Map a flat row into nested contacts

Prompt

Read users with a command, convert each row, then build a `contacts` list from `contact_name1` and `contact_name2`.

Expected

record User
{
    public int id { get; set; }
    public string username { get; set; } = "";
    public string email { get; set; } = "";
    public string contact_name1 { get; set; } = "";
    public string contact_name2 { get; set; } = "";
    public List<Contact> contacts { get; set; } = [];

    public record Contact
    {
        public string name { get; set; } = "";
    }
}

var ds = Datasource.Default;
var command = await ds.CreateCommand();
await using var reader = await command.ExecuteReader(
    "SELECT contact_name1, contact_name2, email, id, username FROM users");
var converter = new MetaDataRowTypeConverter<User>();
TransformMapper<User, User> UserContactsMapper() =>
    new(
        row =>
        {
            row.contacts =
            [
                new User.Contact { name = row.contact_name1 },
                new User.Contact { name = row.contact_name2 }
            ];
            return row;
        },
        user =>
        {
            user.contact_name1 = user.contacts.ElementAtOrDefault(0)?.name ?? "";
            user.contact_name2 = user.contacts.ElementAtOrDefault(1)?.name ?? "";
            return user;
        });
var mapper = UserContactsMapper();
var users = await reader.Read().Select(row => mapper.Map(converter.Convert(row))).ToListAsync();

Select a contact and its type

Prompt

Write a `SelectStatement` for `Contact.FirstName` and `ContactType.Name`, inner-joining `Contact` to `ContactType` on `Contact.ContactTypeId` = `ContactType.Id`.

Expected

new SelectStatement(new()
{
    Table = "Contact",
    Fields = ["Contact.FirstName AS FirstName", "ContactType.Name AS ContactTypeName"],
    Joins = [Join.Inner("ContactType", "Contact.ContactTypeId", "ContactType.Id")],
});

Map a legacy table and a unique key

Prompt

The type stays `user`, but the stored table is `OldUsersTbl`. `username` is unique and stored as `LoginNm`, `id` as `UsrID`, and `is_enabled` as `ActvFlg`. Add `datasource.yaml` mappings and generate.

Expected

version: 1.0.0
types:
  - user:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - username:
            type: string
            size: 128
        - is_enabled:
            type: boolean
version: 1.0.0
includes:
  - types:
      filter: tag == "datasource_type"
types:
  - user:
      mapping: OldUsersTbl
      fields:
        - id:
            mapping: UsrID
        - username:
            is_unique: true
            mapping: LoginNm
        - is_enabled:
            mapping: ActvFlg
npx @deterministic-code/datasources-cli generate all --dialect typescript
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

User created = await ds.Users.Create(new User { Username = "ada", IsEnabled = true });
User? loaded = await ds.Users.ReadSingle();
created.Username = "ada-v2";
Response updated = await ds.Users.Update(created);
Response deleted = await ds.Users.Delete(created);

Features

Parity across C#, Rust, and TypeScript. Standard modules first; opinionated repositories above them.

Feature stack

Feature C# Rust TypeScript
Repository Generation ✅ ✅ ✅
Datasource Factory ✅ ✅ ✅
Datasource ✅ ✅ ✅
Mappers ✅ ✅ ✅
Type Converters ✅ ✅ ✅
Connection ✅ ✅ ✅
Transaction ✅ ✅ ✅
Command ✅ ✅ ✅
Reader ✅ ✅ ✅
Statements ✅ ✅ ✅
Repositories ✅ ✅ ✅
Readonly Repository ✅ ✅ ✅
CRUD Repository ✅ ✅ ✅
Bulk Repository ✅ ✅ ✅
Repository Factories ✅ ✅ ✅

Repository Generation

generate all reads a Types Specification and writes the types, the repositories, and SQL for one language. setup and generate wire the generated registry so Datasource.Default / Datasource.default / default() binds each plural name.

See Generate for flags and output paths.

User

A Types Specification is the deterministic directory. types.yaml lists each entity. Tag a type datasource_type when it is a table.

version: 1.0.0
types:
  - user:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - username:
            type: string
            size: 128
        - is_enabled:
            type: boolean

generate all writes the User type, the Users / users / users() repository, and SQL.

C#
npx @deterministic-code/datasources-cli generate all --dialect csharp
Rust
npx @deterministic-code/datasources-cli generate all --dialect rust
TypeScript
npx @deterministic-code/datasources-cli generate all --dialect typescript

generate all writes the SQL migrations and runs migrate-up against DATASOURCE_URL. --no-migrations skips the files. --no-auto-migrate-up skips applying them. See Migrations to write, apply, or roll back a migration on its own.

Create / create / create returns that entity. ReadSingle / readSingle / read_single returns the same entity. Update and Delete return Response.

C#
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

User created = await ds.Users.Create(new User { Username = "ada", IsEnabled = true });
User? loaded = await ds.Users.ReadSingle();
created.Username = "ada-v2";
Response updated = await ds.Users.Update(created);
Response deleted = await ds.Users.Delete(created);
Rust
use datasources::{default, Response, Row, Value};

let ds = default()?;
let users = ds.as_ref().users();
let mut created = Row::new();
created.insert("username".into(), Value::from("ada"));
created.insert("is_enabled".into(), Value::from(true));
let mut created = users.create(&mut created).await?;
let loaded = users.read_single(None, None, None).await?;
created.insert("username".into(), Value::from("ada-v2"));
let updated: Response = users.update(&created, None).await?;
let deleted: Response = users.delete(&created, None).await?;
TypeScript
import { Datasource } from "@deterministic-code/datasources";

const ds = Datasource.default;

const created = await ds.users.create({ id: 0, username: "ada", isEnabled: true });
const loaded = await ds.users.readSingle();
created.username = "ada-v2";
const updated = await ds.users.update(created);
const deleted = await ds.users.delete(created);

Users and roles

The same user table, plus role and a user_role junction tagged many_to_many. A roles list on user that references user_role.user_id generates a linked enrichment: AddLinked / addLinked / add_linked.

version: 1.0.0
types:
  - user:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - username:
            type: string
            size: 128
        - is_enabled:
            type: boolean
        - roles:
            type: role[]
            references: user_role.user_id
  - role:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - name:
            type: string
            size: 64
  - user_role:
      tags:
        - datasource_type
        - many_to_many
      fields:
        - user_id:
            references: user.id
        - role_id:
            references: role.id
C#
npx @deterministic-code/datasources-cli generate all --dialect csharp
Rust
npx @deterministic-code/datasources-cli generate all --dialect rust
TypeScript
npx @deterministic-code/datasources-cli generate all --dialect typescript

generate all writes the SQL migrations and runs migrate-up against DATASOURCE_URL. --no-migrations skips the files. --no-auto-migrate-up skips applying them. See Migrations to write, apply, or roll back a migration on its own.

Create a role, then a user that lists that role. Create / create / create writes the junction row. ReadSingle / readSingle / read_single fills Roles / roles / roles. Delete removes the user and the junction; the role row stays.

C#
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

Role admin = await ds.Roles.Create(new Role { Name = "admin" });
User created = await ds.Users.Create(new User
{
    Username = "ada",
    IsEnabled = true,
    Roles = [admin],
});
User? loaded = await ds.Users.ReadSingle();
Response deleted = await ds.Users.Delete(created);
Rust
use datasources::{default, encode_rows, Response, Row, Value};

let ds = default()?;
let roles = ds.as_ref().roles();
let mut admin = Row::new();
admin.insert("name".into(), Value::from("admin"));
let admin = roles.create(&mut admin).await?;
let users = ds.as_ref().users();
let mut created = Row::new();
created.insert("username".into(), Value::from("ada"));
created.insert("is_enabled".into(), Value::from(true));
created.insert("roles".into(), encode_rows(&[admin]));
let created = users.create(&mut created).await?;
let loaded = users.read_single(None, None, None).await?;
let deleted: Response = users.delete(&created, None).await?;
TypeScript
import { Datasource } from "@deterministic-code/datasources";

const ds = Datasource.default;

const admin = await ds.roles.create({ id: 0, name: "admin" });
const created = await ds.users.create({
  id: 0,
  username: "ada",
  isEnabled: true,
  roles: [admin],
});
const loaded = await ds.users.readSingle();
const deleted = await ds.users.delete(created);

Legacy tables

When datasource.yaml is omitted, generate maps a plural snake table and snake columns. An explicit datasource.yaml still wins. mapping on a type is the stored table. mapping on a field is the stored column. is_unique writes a unique index.

The type stays user. The stored table is OldUsersTbl. Username is unique and stored as LoginNm.

version: 1.0.0
types:
  - user:
      tags:
        - datasource_type
      fields:
        - id:
            type: integer
            is_id: true
        - username:
            type: string
            size: 128
        - is_enabled:
            type: boolean
version: 1.0.0
includes:
  - types:
      filter: tag == "datasource_type"
types:
  - user:
      mapping: OldUsersTbl
      fields:
        - id:
            mapping: UsrID
        - username:
            is_unique: true
            mapping: LoginNm
        - is_enabled:
            mapping: ActvFlg
C#
npx @deterministic-code/datasources-cli generate all --dialect csharp
Rust
npx @deterministic-code/datasources-cli generate all --dialect rust
TypeScript
npx @deterministic-code/datasources-cli generate all --dialect typescript

generate all writes the SQL migrations and runs migrate-up against DATASOURCE_URL. --no-migrations skips the files. --no-auto-migrate-up skips applying them. See Migrations to write, apply, or roll back a migration on its own.

The generated repository maps those stored names. The CRUD call still uses Username / username / username.

C#
using Backend.Types.Datasource;

dynamic ds = Datasource.Default;

User created = await ds.Users.Create(new User { Username = "ada", IsEnabled = true });
User? loaded = await ds.Users.ReadSingle();
created.Username = "ada-v2";
Response updated = await ds.Users.Update(created);
Response deleted = await ds.Users.Delete(created);
Rust
use datasources::{default, Response, Row, Value};

let ds = default()?;
let users = ds.as_ref().users();
let mut created = Row::new();
created.insert("username".into(), Value::from("ada"));
created.insert("is_enabled".into(), Value::from(true));
let mut created = users.create(&mut created).await?;
let loaded = users.read_single(None, None, None).await?;
created.insert("username".into(), Value::from("ada-v2"));
let updated: Response = users.update(&created, None).await?;
let deleted: Response = users.delete(&created, None).await?;
TypeScript
import { Datasource } from "@deterministic-code/datasources";

const ds = Datasource.default;

const created = await ds.users.create({ id: 0, username: "ada", isEnabled: true });
const loaded = await ds.users.readSingle();
created.username = "ada-v2";
const updated = await ds.users.update(created);
const deleted = await ds.users.delete(created);

Languages and Datasource Providers {#languages}

Pick a language, then a driver. Each driver section lists Information / information / information, Overrides, and RowTypeConverters / rowTypeConverters / row_type_converters from that language's IDriver / IDriver / Driver.

C# {#csharp}

← Languages and Datasource Providers

Drivers for C#. Click a tile for Information, Overrides, and RowTypeConverters from that IDriver.

SQLite {#csharp-sqlite}

← C# drivers

Information
  • Embedded SQL database for local development and tests.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT last_insert_rowid() AS {column}
  • Overrides CreateTableStatement type mapping (int→INTEGER, bool→BOOLEAN, uuid/string/datetime→TEXT, decimal→NUMERIC, float→REAL, binary→BLOB)
RowTypeConverters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
ChangeLogs

None.

MySQL {#csharp-mysql}

← C# drivers

Information
  • MySQL / MariaDB server driver.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT LAST_INSERT_ID() AS {column}
  • Overrides CreateTableStatement identity to AUTO_INCREMENT
  • Overrides CreateTableStatement type mapping (int→INT, uuid→CHAR(36), string/datetime→TEXT, bool→TINYINT, decimal→DECIMAL(18,4), float→DOUBLE, binary→BLOB)
RowTypeConverters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
ChangeLogs

None.

PostgreSQL {#csharp-postgres}

← C# drivers

Information
  • PostgreSQL server driver.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT lastval() AS {column}
  • Overrides CreateTableStatement identity to GENERATED BY DEFAULT AS IDENTITY
  • Overrides CreateTableStatement boolean defaults (0/1 → TRUE/FALSE)
  • Overrides CreateTableStatement type mapping (int→INTEGER, uuid→UUID, string→TEXT, bool→BOOLEAN, decimal→NUMERIC, float→DOUBLE PRECISION, datetime→TIMESTAMPTZ, binary→BYTEA)
RowTypeConverters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
ChangeLogs

None.

Rust {#rust}

← Languages and Datasource Providers

Drivers for Rust. Click a tile for information, Overrides, and row_type_converters from that Driver.

SQLite {#rust-sqlite}

← Rust drivers

information
  • Embedded SQL database for local development and tests.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT last_insert_rowid() AS {column}
  • Overrides CreateTableStatement type mapping (int→INTEGER, bool→BOOLEAN, uuid/string/datetime→TEXT, decimal→NUMERIC, float→REAL, binary→BLOB)
row_type_converters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
change_logs

None.

MySQL {#rust-mysql}

← Rust drivers

information
  • MySQL / MariaDB server driver.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT LAST_INSERT_ID() AS {column}
  • Overrides CreateTableStatement identity to AUTO_INCREMENT
  • Overrides CreateTableStatement type mapping (int→INT, uuid→CHAR(36), string/datetime→TEXT, bool→TINYINT, decimal→DECIMAL(18,4), float→DOUBLE, binary→BLOB)
row_type_converters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
change_logs

None.

PostgreSQL {#rust-postgres}

← Rust drivers

information
  • PostgreSQL server driver.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT lastval() AS {column}
  • Overrides CreateTableStatement identity to GENERATED BY DEFAULT AS IDENTITY
  • Overrides CreateTableStatement boolean defaults (0/1 → TRUE/FALSE)
  • Overrides CreateTableStatement type mapping (int→INTEGER, uuid→UUID, string→TEXT, bool→BOOLEAN, decimal→NUMERIC, float→DOUBLE PRECISION, datetime→TIMESTAMPTZ, binary→BYTEA)
row_type_converters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
change_logs

None.

TypeScript {#typescript}

← Languages and Datasource Providers

Drivers for TypeScript. Click a tile for information, Overrides, and rowTypeConverters from that IDriver.

SQLite {#typescript-sqlite}

← TypeScript drivers

information
  • Embedded SQL database for local development and tests.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT last_insert_rowid() AS {column}
  • Overrides CreateTableStatement type mapping (int→INTEGER, bool→BOOLEAN, uuid/string/datetime→TEXT, decimal→NUMERIC, float→REAL, binary→BLOB)
rowTypeConverters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
changeLogs

None.

MySQL {#typescript-mysql}

← TypeScript drivers

information
  • MySQL / MariaDB server driver.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT LAST_INSERT_ID() AS {column}
  • Overrides CreateTableStatement identity to AUTO_INCREMENT
  • Overrides CreateTableStatement type mapping (int→INT, uuid→CHAR(36), string/datetime→TEXT, bool→TINYINT, decimal→DECIMAL(18,4), float→DOUBLE, binary→BLOB)
rowTypeConverters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
changeLogs

None.

PostgreSQL {#typescript-postgres}

← TypeScript drivers

information
  • PostgreSQL server driver.
  • Full connection, command, transaction, and repository support.
Overrides
  • Overrides SelectInsertedIdStatement to SELECT lastval() AS {column}
  • Overrides CreateTableStatement identity to GENERATED BY DEFAULT AS IDENTITY
  • Overrides CreateTableStatement boolean defaults (0/1 → TRUE/FALSE)
  • Overrides CreateTableStatement type mapping (int→INTEGER, uuid→UUID, string→TEXT, bool→BOOLEAN, decimal→NUMERIC, float→DOUBLE PRECISION, datetime→TIMESTAMPTZ, binary→BYTEA)
rowTypeConverters
  • bool
  • datetime
  • uuid
  • decimal
  • binary
changeLogs

None.

Documentation

Articles

Code Reference

Command Line (CLI)

Writing Providers

Advanced

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages