{"id":13801069,"url":"https://github.com/scalamolecule/molecule","last_synced_at":"2025-10-24T10:31:01.270Z","repository":{"id":89466639,"uuid":"529813636","full_name":"scalamolecule/molecule","owner":"scalamolecule","description":"Molecule translates custom Scala code to database queries for multiple databases.","archived":false,"fork":false,"pushed_at":"2025-01-03T13:37:07.000Z","size":10359,"stargazers_count":19,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-01-29T09:51:15.365Z","etag":null,"topics":["database","datomic-database","h2-database","jdbc","mariadb","mariadb-database","mysql","mysql-database","postgresql","scala"],"latest_commit_sha":null,"homepage":"http://scalamolecule.org","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/scalamolecule.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-28T09:13:41.000Z","updated_at":"2025-01-03T13:35:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"ef46a3e8-cbfb-48bc-a782-7b8417ba4ff4","html_url":"https://github.com/scalamolecule/molecule","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalamolecule%2Fmolecule","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalamolecule%2Fmolecule/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalamolecule%2Fmolecule/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scalamolecule%2Fmolecule/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scalamolecule","download_url":"https://codeload.github.com/scalamolecule/molecule/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237950826,"owners_count":19392666,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","datomic-database","h2-database","jdbc","mariadb","mariadb-database","mysql","mysql-database","postgresql","scala"],"created_at":"2024-08-04T00:01:19.212Z","updated_at":"2025-10-24T10:31:01.252Z","avatar_url":"https://github.com/scalamolecule.png","language":"Scala","funding_links":[],"categories":["Table of Contents"],"sub_categories":["Database"],"readme":"![Molecule logo](project/resources/Molecule-logo.png)\n\n\n\nMolecule is a Scala 3 library for querying and updating SQL databases using a type-safe DSL — not one for SQL, but one generated from your own domain structure.\n\nMost libraries offer a DSL to express _their_ concepts — like SQL or JSON. Molecule inverts that: it builds a DSL directly from your domain structure, letting you model and query data in your own terms.\n\nAfter defining your domain structure, Molecule generates boilerplate code for your custom DSL. You can then declare *what* data you want using this DSL, and Molecule handles *how* to retrieve or modify it.\n\nInstead of stitching SQL by hand, you compose molecules — immutable, type-safe data models that intuitively describe the structure of the data you’re working with - using the words or your domain.\n\nFor instance, get name, age and street address of persons with this molecule\n```scala\n// Query in your domain terms\nPerson.name.age.Address.street\n```\ninstead of writing\n```sql\n-- Equivalent SQL\nSELECT\n  Person.name,\n  Person.age,\n  Address.street\nFROM Person\n  JOIN Address \n    ON Person.address = Address.id;\n```\n\nThe molecule then gets data back with full static typing:\n```scala\nval persons: List[(String, Int, String)] =\n  Person.name.age.Address.street.query.get\n```\nData can also be fetched asynchronously in a `Future`, cats `IO` or `ZIO`.\n\n\n## Why Molecule?\n\n- Type-safe from end to end — no string queries, no surprises\n- Write in your own domain language — not SQL\n- Declarative, not imperative — express intent, not mechanics\n- Composable and immutable — functional by design\n- Cross-platform — JVM and Scala.js with built-in RPC and serialization\n- Same behavior across backends — Postgres, MySQL, MariaDB, SQLite, H2\n\n\n## Quick start\n\nClone [molecule-samples](https://github.com/scalamolecule/molecule-samples) to get started:\n```\ngit clone https://github.com/scalamolecule/molecule-samples.git\n```\n\n\n## Core Concepts\n\n### Domain Structure\n\nFirst, define the entities and attributes of your domain. This structure allows Molecule to generate a minimal and precise DSL tailored to your domain. Here's an example:\n\n```scala\nobject MyDomain extends DomainStructure { \n\n  trait Person {\n    val name     = oneString\n    val age      = oneInt\n    val birthday = oneLocalDate \n    val address  = manyToOne[Address]\n  }\n\n  trait Address {\n    val street = oneString\n    val zip    = oneString\n    val city   = oneString\n  }\n}\n```\nThen run `sbt moleculeGen` - and Molecule generates the necessary DSL code to let you write molecules.\n\n\n### Data Model\n\nNow you can model data as we saw above. Behind the scenes, for every step in the composition of a molecule - or Data Model, two things happen:\n\n1. The result type is extended by collecting attribute types into a composed tuple.\n2. An immutable Data Model is built in order to generate SQL queries and mutations.\n\nOnce you've composed the desired data model, you can call `query.get` on it to fetch data, or one of the transaction commands:\n\n- `save.transact`\n- `insert(data...).transact` - multiples rows of data can be inserted\n- `update.transact`\n- `upsert.transact` (insert if not found)\n- `delete.transact`\n\n\n### Declarative, not Imperative\n\nInstead of thinking in SQL terms (tables, joins, indexes), Molecule encourages staying in the mental model of the domain: what domain data are you trying to model?\n\nThis separation between *declarative intent* and *imperative implementation* is a core principle of Molecule. You express intent; Molecule translates it to optimized SQL queries and mutations.\n\n## Supported Databases\n\nMolecule supports:\n\n- PostgreSQL\n- SQLite\n- MySQL\n- MariaDB\n- H2\n- (more can easily be added...)\n\nAll Molecule queries behave identically across these databases. Each backend passes the same SPI compliance test suite with ~2000 tests.\n\n\n## Features\n\n- Scala 3.7.3 support (JVM + Scala.js)\n- Type-safe and composable molecules\n- Synchronous and asynchronous APIs:\n    - `Future`, `cats.effect.IO`, `ZIO`\n- Rich query capabilities:\n    - Filtering and aggregation\n    - Sorting and pagination (offset/cursor)\n    - Optional/nested relationships\n    - Validation\n    - Subscriptions\n- No macros\n- No complex type class implicit hierarchies\n- Maximum IDE type inference\n- No JSON setup for Scala.js — fully automatic binary serialization via [Boopickle](https://github.com/suzaku-io/boopickle)\n\n\n## Examples\n\n### Query\n\nSynchronous (replace `postgres` to use any of the other databases):\n```scala\nimport molecule.db.postgres.sync._\n\nval persons: List[(String, Int, String)] =\n  Person.name.age.Address.street.query.get\n```\n\nAsynchronous (Future):\n```scala\nimport molecule.db.postgres.async._\n\nval persons: Future[List[(String, Int, String)]] =\n  Person.name.age.Address.street.query.get\n```\n\nZIO:\n```scala\nimport molecule.db.postgres.Zio._\n\nval persons: ZIO[Conn, MoleculeError, List[(String, Int, String)]] =\n  Person.name.age.Address.street.query.get\n```\n\nIO:\n```scala\nimport molecule.db.postgres.io._\n\nval persons: cats.effect.IO[List[(String, Int, String)]] =\n  Person.name.age.Address.street.query.get\n```\n\n### Transact\n\nSave one entity:\n```scala\nPerson\n  .name(\"Bob\")\n  .age(42)\n  .Address\n  .street(\"Baker st\")\n  .save.transact\n```\n\nInsert multiple entities:\n```scala\nPerson.name.age.Address.street.insert(\n  (\"Bob\", 42, \"Baker st\"),\n  (\"Liz\", 38, \"Bond road\")\n).transact\n```\n\nUpdate:\n```scala\nPerson(bobId).age(43).update.transact\n```\n\nDelete:\n```scala\nPerson(bobId).delete.transact\n```\n\n\n### Inspect\n\nMolecule doesn't hide its inner workings in a magic black box. You can always `inspect` what a molecule translates to:\n\n```scala\nPerson.name(\"Bob\").age(42).save.inspect\n```\n\nPrints:\n```\nSAVE:\nDataModel(...)\n\nINSERT INTO Person (\n  name,\n  age\n) VALUES (?, ?)\n```\n\nQuery inspection:\n```scala\nPerson.name.age.query.inspect\n```\n\nPrints:\n```\nQUERY:\nDataModel(...)\n\nSELECT DISTINCT\n  Person.name,\n  Person.age\nFROM Person\nWHERE\n  Person.name IS NOT NULL AND\n  Person.age  IS NOT NULL;\n```\nThis way you can always inspect and see what will be sent to the database. And if you want to tweak a query or mutation, Molecule offers fallback alternatives too:\n\n\n### Fallback\n\nYou’re always in control. Molecule provides full fallbacks — inspect what it does, or drop down to raw SQL/mutations when needed.\n\nQuery fallback:\n```scala\nrawQuery(\n  \"\"\"SELECT DISTINCT\n     |  Person.name,\n     |  Person.age\n     |FROM Person\n     |WHERE\n     |  Person.name IS NOT NULL AND\n     |  Person.age  IS NOT NULL;\n     |\"\"\".stripMargin,\n  true // print debug info\n).map(_ ==\u003e List(List(\"Bob\", 42)))\n```\nThis gives you back a List of rows where each row is a `List[Any]`. You'll have to cast the data yourself then. Or you can use another SQL library for manual queries, or even raw JDBC.\n\nTransaction fallback:\n```scala\nrawTransact(\n  \"\"\"INSERT INTO Person (\n     |  name,\n     |  age\n     |) VALUES ('Bob', 42)\n     |\"\"\".stripMargin)\n```\nNote that only static input values are supported.\n\nMost of the time you'll likely have enough expressiveness with Molecule without having to resort to manual SQL writing.\n\n\n#### Quick inspect\n\nThere's also a shorthand alternative `i` that you can add like in `query.i.get` in order to both inspect and perform the query. You can use it on the mutations too, but be aware that the mutation will still perform! `inspect` is more safe to use on mutations since it only inspects.\n\n\n\n## SBT Setup\n\n`project/build.properties`:\n```\nsbt.version = 1.11.6\n```\n\n`project/plugins.sbt`:\n```scala\naddSbtPlugin(\"org.scalamolecule\" % \"sbt-molecule\" % \"1.21.1\")\n```\n\n`build.sbt`:\n```scala\nlazy val yourProject = project.in(file(\"app\"))\n  .enablePlugins(MoleculePlugin)\n  .settings(\n    libraryDependencies ++= Seq(\n      // import database(s) that you need\n      \"org.scalamolecule\" %% \"molecule-db-h2\" % \"0.26.0\",\n      \"org.scalamolecule\" %% \"molecule-db-mariadb\" % \"0.26.0\",\n      \"org.scalamolecule\" %% \"molecule-db-mysql\" % \"0.26.0\",\n      \"org.scalamolecule\" %% \"molecule-db-postgresql\" % \"0.26.0\",\n      \"org.scalamolecule\" %% \"molecule-db-sqlite\" % \"0.26.0\",\n    )\n  )\n```\n\nUse `%%%` instead of `%%` for Scala.js.\n\n\n## Explore code\n\nThe `dbCompliance` module in this repo has several domain structure definitions and ~2000 tests that show all details of\nhow molecule can be used. This forms the tests that each database implementation needs to comply with\nin order to offer all functionality of Molecule and be a compliant implementation.\n\nFirst, clone the molecule project to your computer (or `git pull` to get the latest changes):\n```\ngit clone https://github.com/scalamolecule/molecule.git\ncd molecule\n```\nThen run some tests on either ScalaJVM or ScalaJS:\n\n\n### Run JVM tests\n\nMake sure Docker is running to run tests for Postgres, SQlite, Mysql and MariaDB. H2 can be run in memory for tests. On a mac you can for instance start Docker Desktop.\n\nRun the tests on the jvm with a databases of your choice:\n\n    sbt dbH2JVM/test\n    sbt dbMariaDBJVM/test\n    sbt dbMySQLJVM/test\n    sbt dbPostgreSQLJVM/test\n    sbt dbSQliteJVM/test\n\n\n### Run JS tests\n\nTo test using molecules from ScalaJS, you need to have a ScalaJVM backend server running in a separate process that can receive the queries and send data back to ScalaJS.\n\nIn the `server` module you can see 5 different minimal Tapir backend setups that you can start out from. In one process you can start up one of those backends where you will be asked which backend and database that you want to use:\n\n```\nsbt server/Test/run\n\nPlease choose a database and a server backend to test the Molecule RPC API:\n\n  1  H2\n  2  MariaDB\n  3  MySQL\n  4  PostgreSQL\n  5  SQlite\n\nDatabase: 1\n\n  1  Http4s\n  2  Netty\n  3  Pekko\n  4  Play\n  5  ZioHttp\n\nServer: 2\n\nPress ENTER to stop the server.\n✅ Netty server running on http://localhost:8080 for H2\n\n// run tests in other process...\n\n🛑 Shutting down server...\n```\nNow we have a backend running on ScalaJVM ready to take care of your molecule queries from ScalaJS using the H2 database!\n\nIn another process you can then run one of the following commands to run the coreTests on ScalaJS with the database of your choice:\n\n```\nsbt dbH2JS/test\nsbt dbMariaDBJS/test\nsbt dbMySQLJS/test\nsbt dbPostgreSQLJS/test\nsbt dbSQliteJS/test\n```\nThe tests are then automatically fetching data from the running backend - Molecule takes care of marshalling and fetching transparently with boopickle binary serialization!\n\n\n## Author\n\nMarc Grue\n\n## License\n\nApache License 2.0","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalamolecule%2Fmolecule","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscalamolecule%2Fmolecule","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscalamolecule%2Fmolecule/lists"}