{"id":17988531,"url":"https://github.com/heyvito/clsql","last_synced_at":"2025-06-13T09:36:07.328Z","repository":{"id":62431653,"uuid":"255990130","full_name":"heyvito/clsql","owner":"heyvito","description":"🗂 A toolchain to work with databases and SQL for Clojure applications.","archived":false,"fork":false,"pushed_at":"2020-07-27T20:50:23.000Z","size":87,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-07-23T21:03:51.458Z","etag":null,"topics":["clojure","database","migration","query","sql"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/heyvito.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}},"created_at":"2020-04-15T17:30:45.000Z","updated_at":"2020-07-27T20:50:26.000Z","dependencies_parsed_at":"2022-11-01T21:00:42.290Z","dependency_job_id":null,"html_url":"https://github.com/heyvito/clsql","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyvito%2Fclsql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyvito%2Fclsql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyvito%2Fclsql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/heyvito%2Fclsql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/heyvito","download_url":"https://codeload.github.com/heyvito/clsql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222099752,"owners_count":16931479,"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":["clojure","database","migration","query","sql"],"created_at":"2024-10-29T19:11:56.597Z","updated_at":"2024-10-29T19:11:56.684Z","avatar_url":"https://github.com/heyvito.png","language":"Clojure","readme":"# clsql\n![](https://github.com/heyvito/clsql/workflows/Test/badge.svg)\n[![cljdoc badge](https://cljdoc.org/badge/clsql/clsql)](https://cljdoc.org/d/clsql/clsql/CURRENT)\n[![Clojars Project](https://img.shields.io/clojars/v/clsql.svg)](https://clojars.org/clsql)\n\n**clsql** provides a toolchain to work with databases and SQL for Clojure\napplications.\u003cbr/\u003e\nIt provides facilities to work with migrations and queries, allowing teams to\nhave reproducible migrations and easier to maintain queries, leaving SQL out\nof Clojure.\n\n## Reasoning\nClojure is a great tool to build DSLs, that's clear to every Clojure developer.\nBut maintaining raw SQL within Clojure can lead to cluttered code, and using\nDSLs for such can make learning curves for new team members steeper, or\nintroduce hard-to-find bugs. For those reasons, **clsql** was built.\n\nThis library is heavily inspired by some of\n[yesql](https://github.com/krisajenkins/yesql) features.\n\n## Installation\n\nAdd the following dependency to your `project.clj` file:\n\n```clojure\n[clsql \"0.3.0\"]\n```\n\n### Dependencies\n\n**clsql** does not provide abstractions to SQL. It simply parses files in a\npredetermined format, in a set of directories following a [convention over configuration](https://en.wikipedia.org/wiki/Convention_over_configuration)\napproach, and wraps contents and annotations of those files into Clojure\nfunctions. In order to access a database, you will need a **driver**:\n\n|  Database  | Driver                                       |\n|------------|----------------------------------------------|\n| PostgreSQL | `[org.postgresql/postgresql \"42.2.12.jre7\"]` |\n\n\u003e **Notice**: Currently, this library has been tested exclusively with PostgreSQL.\nIn case you use another RDBMS, feel free to test it and open a pull request to\nupdate this table.\n\n\n## Usage\n\nAs mentioned previously, `clsql` handles both migrations and queries. Developers\nare free to use only a single facility, or both of them.\n\n### Initial configuration\nLibrary defaults can be changed by changing\n[`atom`](https://clojure.org/reference/atoms)s defined by the `clsql.config`\nnamespace:\n\n| Atom                     | Required By         | Description   |\n|--------------------------|---------------------|---------------|\n| `migrations-directory`   | Migrations          | Indicates the directory in which the library will look for migration files. Defaults to `\"resources/db/migrations\"` |\n| `queries-directory`      | Querying            | Indicates the directory in which the library will look for query files. Defaults to `\"resources/db/queries\"`|\n| `database-configuration` | Migrations/Querying | Defines configurations to connect to the target database server. |\n\n`database-configuration` is required by both facilities. It is required for\nquerying if you intend to use the simpler, non-star query functions. For further\ninformation, refer to [Passing Extra Options](#Passing-Extra-Options)\n\n### Migrations\nIn order to use any feature provided by the Migrations facility, your\napplication's entrypoint must be updated to give control over to `clsql` to\nperform command-line parsing and execute any required operation. For such,\ncheck the snippet below:\n\n```clojure\n(ns myapplication.core)\n\n(defn- configure-clsql []\n    (reset! clsql.config/migrations-directory \"./another/directory\"))\n\n(defn -main []\n  (configure-clsql)\n  (when-not (clsql.cli-handler/handle-command-line-args)\n    (comment Your Application initialisation procedures goes here)))\n```\n\n\u003e **ProTip™**: Execute any modifications to the library's configuration options\nprior to calling `handle-command-line-args`.\n\n`handle-command-line-args` will return `false` in case it could not detect\nany operation request. The following operations can be executed:\n\n| Command | Description |\n|---------|-------------|\n| `create-migration NAME` | Creates a new migration stub in the configured `migrations-directory`. `NAME` can only contain letters (A-Za-z), numbers (0-9) and dashes (-) |\n| `db-migrate` | Executes every pending migration |\n| `db-rollback` | Rolls-back the database by executing the `down` section of the last migration. |\n| `db-rollback VERSION` | Rolls-back the database by executing the `down` section of every migration created after `VERSION` |\n| `db-migration-status` | Displays a table listing all migrations and their respective statuses |\n\n#### Working with Migrations\nEach migration created with `create-migration` is composed by two sections:\n`up` and `down`.\n`up` is required, and must contain every SQL command needed to migrate your\ndatabase to the next version.\n`down` is optional. When defined, it must contain every SQL command needed to\nrevert changes made by `up`. When undefined, a migration will be considered\nirreversible. Trying to rollback it will result in an error, indicating that\nit cannot be reversed. Below you can find a migration example:\n\n```sql\n-- This creates a new `users` table.\n-- Notice: Comments are allowed anywhere. The only thing clsql cares about\n-- is region separators, indicated with `--;; \u003cregion\u003e`, where `\u003cregion\u003e`\n-- may be `up` or `down`.\n\n--;; up\nCREATE TABLE users\n    (id INTEGER NOT NULL PRIMARY KEY,\n     name VARCHAR NOT NULL, -- We're allowing comments to be here as well.\n     email TEXT NOT NULL UNIQUE);\n\n--\n-- This is another comment, between regions.\n--\n\n--;; down\nDROP TABLE users;\n```\n\n#### Migration safety\nMigrations are executed within a transaction. This way, in case anything goes\nwrong, all changes will be automatically reverted.\n\n\u003e **WARNING**: Not every RDBMS accepts schema modifications within transactions.\n\n### Queries\n\nPlain SQL files defines queries where each file may contain an arbitrary number\nof queries. Each query must contain at least a name, but can also include\nadditional information, such as a documentation, and modifiers.\n\n#### Defining queries\n\nTo define queries, first ensure the directory configured by\n[`clsql.config/queries-directory`](#initial-configuration) exists. Then, create\na new file, for examples here, assume a file named `users.sql`. Then, define a\nnew query by prefixing it with a name:\n\n```sql\n--\u003e active-users\n```\n\nNames must be indicated by the `--\u003e` prefix, followed by how one wants to\nreference the query on Clojure's side. In the example above, an `active-users`\nquery will be defined. After adding documentation, the same query can be\nrepresented by the following snippet:\n\n```sql\n--\u003e active-users\n-- Returns all users marked as active.\n-- Users are marked as active after confirming their email addresses.\n```\n\nThe code above allows the library to automatically include documentation\nto generated functions, making them available through [`doc`](https://clojuredocs.org/clojure.repl/doc).\n\nThen, write your SQL as you would:\n\n```sql\n--\u003e active-users\n-- Returns all users marked as active.\n-- Users are marked as active after confirming their email addresses.\nSELECT *\nFROM users\nWHERE activated_at IS NOT NULL\n```\n\n#### Importing queries\n\nAfter configuring and defining your SQL, simply import it by using\n`require-query` or `require-queries`:\n\n```clojure\n(ns myapplication.some-namespace\n    :require [clsql.core :refer [require-query]])\n\n(require-query users :refer [active-users])\n\n(defn list-active-users []\n    (active-users))\n```\n\nThe result will be a list of maps of all returned results.\n\nIn the REPL, documentation can be also accessed:\n\n```clojure\n=\u003e (doc active-users)\n-------------------------\nmyapplication.some-namespace/active-users\n([\u0026 args*])\n  Returns all users marked as active. Users are marked as active after\n  confirming their email addresses.\n\n  Arguments\n  ---------\n  args must be a list of keyword-value containing required arguments\n  to execute this function.\n```\n\n#### Queries with placeholders\n\n**clsql** has support to placeholders. Defining them in your SQL allows\ngenerated functions to safely replace them into generated queries, and also\nto perform validations on whether required parameters are provided when invoking\nsuch functions. Let's keep working on our `users.sql` file by adding a new\nquery:\n\n```sql\n--\u003e activated-after\n-- Returns all users activated after a given date.\nSELECT *\nFROM users\nWHERE activated_at \u003e= :date\n```\n\nThe query above declares a placeholder by using the `:NAME` notation. Let's\nimport it and see what happens:\n\n```clojure\n(require-query users :as users)\n\n; require-query accepts :as and :refer, just like `require`. For more\n; information, see require-query and require-queries documentation.\n```\n\nNow, let's try to invoke it without arguments and see how it behaves:\n\n```clojure\n=\u003e (users/activated-after)\nExecution error (IllegalArgumentException) at ...\nMissing parameter(s): :date\n```\n\nThe generated function knows a placeholder was defined, but not provided during\nthe invocation. Thus, an `IllegalArgumentException` was thrown. To fix it, let's\nprovide what it needs:\n\n```clojure\n=\u003e (users/activated-after :date \"2020-01-24\")\n...\n```\n\n### Passing Extra Options\nEach query function exists in two forms, a simpler one, which takes either an\neven number of arguments forming a map or none at all, and another one, with a\nstar (`*`) suffix, that take extra params. For instance, let's see documentation\nfor both versions of our `activated-after` function:\n\n```clojure\n=\u003e (doc users/activated-after)\n-------------------------\nmyapplication.some-namespace/activated-after\n([\u0026 args*])\n  Returns all users activated after a given date.\n\n  Required arg: :date\n\n  Arguments\n  ---------\n  args must be a list of keyword-value containing required arguments\n  to execute this function.\n\n\n=\u003e (doc users/activated-after*)\n-------------------------\nmyapplication.some-namespace/activated-after*\n([db args? \u0026 opts*])\n  Returns all users activated after a given date.\n\n  Required arg: :date\n\n  Arguments\n  ---------\n  db must be a database spec or transaction, as defined by clojure.jdbc\n  args must be a map of required arguments for this query, or nil\n  opts may be a list of options to be passed to clojure.jdbc\n```\n\nOne can note major differences between those functions by only analysing their\narguments; The simpler one wants an arbitrary number of arguments, whilst the\nsecond one wants a `db`, `args` and `opts`. While one may almost always use\nthe non-star function, the second may come handy in case a different database\nthan the one defined by `database-configuration` needs to be accessed.\n`db` must be a db-spec structure just like one would use with\n[`clojure.java.jdbc`](https://github.com/clojure/java.jdbc). It can\nalternativelly be a transaction, obtained from\n[`with-db-transaction`](http://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql.html#using-transactions).\n\n\n## Contributions\n...are more than welcome! \u003cbr/\u003e\nFeel free to fork and open a pull request. Please\ninclude tests, and ensure all tests are green!\n\n## License\n\n```\nMIT License\n\nCopyright (c) 2020 - Victor Gama de Oliveira\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheyvito%2Fclsql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fheyvito%2Fclsql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fheyvito%2Fclsql/lists"}