{"id":13695730,"url":"https://github.com/mandubian/play-autosource","last_synced_at":"2025-05-03T13:33:16.713Z","repository":{"id":8906986,"uuid":"10631017","full_name":"mandubian/play-autosource","owner":"mandubian","description":"Play Autosource : 1mn REST/Crud/Async/Typesafe Datasource for Play Framework","archived":false,"fork":false,"pushed_at":"2015-02-05T07:33:30.000Z","size":543,"stargazers_count":154,"open_issues_count":9,"forks_count":35,"subscribers_count":24,"default_branch":"master","last_synced_at":"2024-08-03T18:19:53.432Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mandubian.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":"2013-06-11T21:09:03.000Z","updated_at":"2024-05-31T13:06:39.000Z","dependencies_parsed_at":"2022-09-20T11:13:29.322Z","dependency_job_id":null,"html_url":"https://github.com/mandubian/play-autosource","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/mandubian%2Fplay-autosource","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandubian%2Fplay-autosource/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandubian%2Fplay-autosource/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mandubian%2Fplay-autosource/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mandubian","download_url":"https://codeload.github.com/mandubian/play-autosource/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224364374,"owners_count":17299059,"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":[],"created_at":"2024-08-02T18:00:32.857Z","updated_at":"2024-11-12T23:30:53.506Z","avatar_url":"https://github.com/mandubian.png","language":"Scala","funding_links":[],"categories":["Resources for old Play version (before 2.6)"],"sub_categories":["Others"],"readme":"#### EXPERIMENTAL / DRAFT\n\n# Play Autosource\n\n## An automatic full REST + Typesafe CRUD abstract Datasource for bootstrapping a Play Framework App\n\nImplementations available for:\n- [ReactiveMongo](http://www.reactivemongo.org) ( _play-autosource:reactivemongo:2.0-SNAPSHOT_ )\n- [Couchbase](http://www.couchbase.com) based on Mathieu Ancelin's [ReactiveCouchbase](http://reactivecouchbase.org/) ( _play-autosource:couchbase:2.0-SNAPSHOT_ )\n- [Datomic](http://www.datomic.com) based on [Pellucid](http://www.pellucidanalytics.com)'s [Datomisca](http://pellucidanalytics.github.io/datomisca/) ( _play-autosource:datomisca:2.0_ )\n- [Slick/JDBC](http://slick.typesafe.com/) based on [Play2-Slick](https://github.com/freekh/play-slick) (thanks to [Loic Descotte](https://github.com/loicdescotte) and [Renato Cavalcanti](https://github.com/rcavalcanti)) ( _play-autosource:slick:2.0-SNAPSHOT_ )\n\n\u003cbr/\u003e\n\n# 2'30 tutorial\n\nHere we go:\n\n### 0' : Create App\n\n```scala\n\u003e play2 new auto-persons\n       _            _\n _ __ | | __ _ _  _| |\n| '_ \\| |/ _' | || |_|\n|  __/|_|\\____|\\__ (_)\n|_|            |__/\n\nplay! 2.1.1 (using Java 1.7.0_21 and Scala 2.10.0), http://www.playframework.org\n\nThe new application will be created in /Users/pvo/zenexity/workspaces/workspace_mandubian/auto-persons\n\nWhat is the application name? [auto-persons]\n\u003e\n\nWhich template do you want to use for this new application?\n\n  1             - Create a simple Scala application\n  2             - Create a simple Java application\n\n\u003e 1\nOK, application auto-persons is created.\n\nHave fun!\n```\n\n### 10' : edit project/Build.scala, add `play-autosource:reactivemongo` dependency\n\n```scala\nval mandubianRepo = Seq(\n  \"Mandubian repository snapshots\" at \"https://github.com/mandubian/mandubian-mvn/raw/master/snapshots/\",\n  \"Mandubian repository releases\" at \"https://github.com/mandubian/mandubian-mvn/raw/master/releases/\"\n)\n\nval appDependencies = Seq()\n\nval main = play.Project(appName, appVersion, appDependencies).settings(\n  resolvers ++= mandubianRepo,\n  libraryDependencies ++= Seq(\n    \"play-autosource\"   %% \"reactivemongo\"       % \"2.1-SNAPSHOT\",\n    \"org.specs2\"        %% \"specs2\"              % \"1.13\"        % \"test\",\n    \"junit\"              % \"junit\"               % \"4.8\"         % \"test\"\n  )\n)\n```\n\n### 30' : Create new ReactiveMongo AutoSource Controller in app/Person.scala\n\n```scala\npackage controllers\n\nimport play.api._\nimport play.api.mvc._\n\n// BORING IMPORTS\n// Json\nimport play.api.libs.json._\nimport play.api.libs.functional.syntax._\n// Reactive JSONCollection\nimport play.modules.reactivemongo.json.collection.JSONCollection\n// Autosource\nimport play.autosource.reactivemongo._\n// AutoSource is Async so imports Scala Future implicits\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport play.api.Play.current\n\n// \u003e\u003e\u003e THE IMPORTANT PART \u003c\u003c\u003c\nobject Persons extends ReactiveMongoAutoSourceController[JsObject] {\n  val coll = db.collection[JSONCollection](\"persons\")\n}\n```\n\n### 50' : Add AutoSource routes at beginning `conf/routes`\n\n```scala\n-\u003e      /person                     controllers.Persons\n```\n\n### 60' : Create `conf/play.plugins` to initialize ReactiveMongo Plugin\n\n```scala\n400:play.modules.reactivemongo.ReactiveMongoPlugin\n```\n\n### 70' : Append to `conf/application.conf` to initialize MongoDB connection\n\n```scala\nmongodb.uri =\"mongodb://localhost:27017/persons\"\n```\n\n### 80' : Launch application\n\n```scala\n\u003e play2 run\n\n[info] Loading project definition from /.../auto-persons/project\n[info] Set current project to auto-persons (in build file:/.../auto-persons/)\n\n[info] Updating {file:/.../auto-persons/}auto-persons...\n[info] Done updating.\n--- (Running the application from SBT, auto-reloading is enabled) ---\n\n[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000\n\n(Server started, use Ctrl+D to stop and go back to the console...)\n[info] Compiling 5 Scala sources and 1 Java source to /.../auto-persons/target/scala-2.10/classes...\n[warn] there were 2 feature warnings; re-run with -feature for details\n[warn] one warning found\n[success] Compiled in 6s\n```\n\n### 100' : Insert your first 2 persons with Curl\n\n```scala\n\u003ecurl -X POST -d '{ \"name\":\"bob\", \"age\":25 }' --header \"Content-Type:application/json\" http://localhost:9000/person --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 33\n\n{\"id\":\"51b868ef31d4002c0bac8ba4\"} -\u003e oh a BSONObjectId\n\n\u003ecurl -X POST -d '{ \"name\":\"john\", \"age\":43 }' --header \"Content-Type:application/json\" http://localhost:9000/person --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 33\n\n{\"id\":\"51b868fa31d4002c0bac8ba5\"}\n```\n\n### 110' : Get all persons\n\n```scala\n\u003ecurl http://localhost:9000/person --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 118\n\n[\n  {\"name\":\"bob\",\"age\":25.0,\"id\":\"51b868ef31d4002c0bac8ba4\"},\n  {\"name\":\"john\",\"age\":43.0,\"id\":\"51b868fa31d4002c0bac8ba5\"}\n]\n```\n\n\n### 115' : Delete one person\n\n```scala\n\u003ecurl -X DELETE http://localhost:9000/person/51b868ef31d4002c0bac8ba4 --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 33\n\n{\"id\":\"51b868ef31d4002c0bac8ba4\"}\n```\n\n### 120' : Verify person was deleted\n\n```scala\n\u003ecurl -X GET http://localhost:9000/person/51b868ef31d4002c0bac8ba4 --include\n\nHTTP/1.1 404 Not Found\nContent-Type: text/plain; charset=utf-8\nContent-Length: 37\n\nID 51b868ef31d4002c0bac8ba4 not found\n```\n\n### 125' : Update person\n\n```scala\n\u003ecurl -X PUT -d '{ \"name\":\"john\", \"age\":35 }' --header \"Content-Type:application/json\" http://localhost:9000/person/51b868fa31d4002c0bac8ba5 --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 33\n\n{\"id\":\"51b868fa31d4002c0bac8ba5\"}\n```\n\n### 130' : Batch insert 2 persons (johnny \u0026 tom) with more properties\n\n```scala\n\u003ecurl -X POST -d '[{ \"name\":\"johnny\", \"age\":15, \"address\":{\"city\":\"Paris\", \"street\":\"rue quincampoix\"} },{ \"name\":\"tom\", \"age\":3, \"address\":{\"city\":\"Trifouilly\", \"street\":\"rue des accidents de poucettes\"} }]' --header \"Content-Type:application/json\" http://localhost:9000/person/batch --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 8\n\n{\"nb\":2}\n```\n\n### 135' : Get all persons whose name begins by \"john\"\n\n```scala\n\u003ecurl -X POST -d '{\"name\":{\"$regex\":\"^john\"}}' --header \"Content-Type:application/json\" http://localhost:9000/person/find --include\n\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 175\n\n[\n  {\"name\":\"john\",\"age\":35.0,\"id\":\"51b868fa31d4002c0bac8ba5\"},\n  {\"id\":\"51b86a1931d400bc01ac8ba8\",\"name\":\"johnny\",\"age\":15.0,\"address\":{\"city\":\"Paris\",\"street\":\"rue quincampoix\"}}\n]\n```\n\n### 140' : Delete all persons\n\n```scala\n\u003ecurl -X DELETE -d '{}' --header \"Content-Type:application/json\" http://localhost:9000/person/batch --include\n\nHTTP/1.1 200 OK\nContent-Type: text/plain; charset=utf-8\nContent-Length: 7\n\ndeleted\n```\n\n### 145' : Take 5' rest\n\u003cbr/\u003e\n### 150' : Done\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n##So what was demonstrated here?\n\nWith Play-Autosource, in a few lines, you obtain :\n\n- **A backed abstract datasource (here implemented for [ReactiveMongo](http://www.reactivemongo.org) but it could be implemented for other DBs)**\n- **All CRUD operations are exposed as pure REST services**\n- **The datasource is typesafe (here `JsObject` but we'll show later that we can use any type)**\n\nIt can be useful to kickstart any application in which you're going to work iteratively on our data models in direct interaction with front-end.\n\nIt could also be useful to Frontend developers who need to bootstrap frontend code with Play Framework application backend. With _Autosource_, they don't have to care about modelizing strictly a datasource on server-side and can dig into their client-side code quite quickly.\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n## Adding constraints \u0026 validation\n\u003eNow you tell me: \"Hey that's stupid, you store directly `JsObject` but my data are structured and must be validated before inserting them\"\n\nYes you're right so let's add some type constraints on our data:\n\n```scala\nobject Persons extends ReactiveMongoAutoSourceController[JsObject] {\n  val coll = db.collection[JSONCollection](\"persons\")\n\n  // we validate the received Json as JsObject because the autosource type is JsObject\n  // and we add classic validations on types\n  override val reader = __.read[JsObject] keepAnd (\n    (__ \\ \"name\").read[String] and\n    (__ \\ \"age\").read[Int](Reads.min(0) keepAnd Reads.max(117))\n  ).tupled\n}\n```\n\nTry it now:\n\n```scala\ncurl -X POST -d '{ \"nameXXX\":\"bob\", \"age\":25 }' --header \"Content-Type:application/json\" http://localhost:9000/person --include\n\nHTTP/1.1 400 Bad Request\nContent-Type: application/json; charset=utf-8\nContent-Length: 62\n\n{\"obj.name\":[{\"msg\":\"validate.error.missing-path\",\"args\":[]}]}\n```\n\nYou can add progressively constraints on your data in a few lines. With `AutoSource`, you don't need to determine immediately the exact shape of your models and you can work with `JsObject` directly as long as you need. Sometimes, you'll even discover that you don't even need a structured model and `JsObject` will be enough. (_but I also advise to design a bit things before implementing ;)_)\n\n\u003e Keep in mind that our sample is based on an implementation for _ReactiveMongo_ so using Json is natural. For other DB, other data structure might be more idiomatic...\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n## Use typesafe models\n\n\u003eNow you tell me: \"Funny but but but `JsObject` is evil because it's not strict enough. I'm a OO developer (maybe abused by ORM gurus when I was young) and my models are case-classes...\"\n\nYes you're right, sometimes, you need more business logic or you want to separate concerns very strictly and your model will be shaped as case-classes.\n\nSo let's replace our nice little `JsObject` by a more serious `case class`.\n\n```scala\n// the model\ncase class Person(name: String, age: Int)\nobject Person{\n  // the famous Json Macro which generates at compile-time a Reads[Person] in a one-liner\n  implicit val fmt = Json.format[Person]\n}\n\n// The autosource... shorter than before\nobject Persons extends ReactiveMongoAutoSourceController[Person] {\n  val coll = db.collection[JSONCollection](\"persons\")\n}\n```\n\nPlease note that I removed the validations I had introduced before because there are not useful anymore: using Json macros, I created an implicit `Format[Person]` which is used implicitly by AutoSource.\n\n\u003e So, now you can see why I consider AutoSource as a *typesafe datasource*.\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n## Let's be front-sexy with AngularJS\n\nYou all know that [AngularJS](http://www.angularjs.org/) is the new kid on the block and that you must use it if you want to be sexy nowadays.\n\nI'm already sexy so I must be able to use it without understanding anything to it and that's exactly what I've done: in 30mn without knowing anything about Angular (but a few concepts), I wrote a dumb CRUD front page plugged on my wonderful `AutoSource`.\n\n\u003cbr/\u003e\n### Client DS in assets/javascripts/persons.js\n\nThis is the most important part of this sample: we need to call our CRUD autosource endpoints from angularJS.\n\nWe are going to use _Angular resources_ for it even if it's not really the best feature of AngularJS. Anyway, in a few lines, it works pretty well in my raw case.\n\n_(thanks to Paul Dijou for reviewing this code because I repeat I don't know angularJS at all and I wrote this in 20mn without trying to understand anything :D)_\n\n```javascript\n\n // my.resources (http://kirkbushell.me/angular-js-using-ng-resource-in-a-more-restful-manner/), in order to support PUT updates\nvar module = angular.module('my.resource', [ 'ngResource' ]);\n\nmodule.factory('Resource', [ '$resource', function ($resource) {\n    return function (url, params, methods) {\n        var defaults = {\n            update: { method: 'put', isArray: false },\n            create: { method: 'post' }\n        };\n\n        methods = angular.extend(defaults, methods);\n\n        var resource = $resource(url, params, methods);\n\n        resource.prototype.$save = function () {\n            if (!this.id) {\n                this.$create();\n            }\n            else {\n                this.$update();\n            }\n        };\n\n        return resource;\n    };\n}]);\n\nvar app =\n  // injects my.resources to support PUT updates\n  angular.module(\"app\", [\"my.resources\"])\n  // creates the Person factory backed by our autosource\n  // Please remark the url person/:id which will use transparently our CRUD AutoSource endpoints\n  .factory('Person', [\"$resource\", function($resource){\n    return $resource('person/:id', { \"id\" : \"@id\" }, { update: { method: 'PUT' }});\n  }])\n  // creates a controller\n  .controller(\"PersonCtrl\", [\"$scope\", \"Person\", function($scope, Person) {\n\n    $scope.createForm = {};\n\n    // retrieves all persons\n    $scope.persons = Person.query();\n\n    // creates a person using createForm and refreshes list\n    $scope.create = function() {\n      var person = new Person({name: $scope.createForm.name, age: $scope.createForm.age});\n      person.$save(function(){\n        $scope.createForm = {};\n        $scope.persons = Person.query();\n      })\n    }\n\n    // removes a person and refreshes list\n    $scope.remove = function(person) {\n      person.$remove(function() {\n        $scope.persons = Person.query();\n      })\n    }\n\n    // updates a person and refreshes list\n    $scope.update = function(person) {\n      person.$update(function() {\n        $scope.persons = Person.query();\n      })\n    }\n}]);\n```\n\n### CRUD UI in index.scala.html\n\nNow let's create our CRUD UI page using angular directives. We need to be able to:\n\n- list persons\n- update/delete each person\n- create new persons\n\n```html\n@(message: String)\n\n@main(\"Welcome to Play 2.1\") {\n\n    \u003cdiv ng-controller=\"PersonCtrl\"\u003e\n      \u003c!-- create form --\u003e\n      \u003cform\u003e\n        \u003clabel for=\"name\"\u003ename:\u003c/label\u003e\u003cinput id=\"name\" ng-model=\"createForm.name\"/\u003e\n        \u003clabel for=\"age\"\u003eage:\u003c/label\u003e\u003cinput id=\"age\" ng-model=\"createForm.age\" type=\"number\"/\u003e\n        \u003cbutton ng-click=\"create()\"\u003eCreate new person\u003c/button\u003e\n      \u003c/form\u003e\n      \u003chr/\u003e\n      \u003c!-- List of persons with update/delete buttons --\u003e\n      \u003ctable\u003e\n        \u003cthead\u003e\u003cth\u003ename\u003c/th\u003e\u003cth\u003eage\u003c/th\u003e\u003cth\u003eactions\u003c/th\u003e\u003c/thead\u003e\n        \u003ctbody\u003e\n          \u003ctr ng-repeat=\"person in persons\"\u003e\n            \u003ctd\u003e\u003cinput ng-model=\"person.name\"/\u003e\u003c/td\u003e\n            \u003ctd\u003e\u003cinput type=\"number\" ng-model=\"person.age\"/\u003e\u003c/td\u003e\n            \u003ctd\u003e\u003cbutton ng-click=\"update(person)\"\u003eUpdate\u003c/button\u003e\u003cbutton ng-click=\"remove(person)\"\u003eDelete\u003c/button\u003e\u003c/td\u003e\n          \u003c/tr\u003e\n        \u003c/tbody\u003e\n      \u003c/table\u003e\n    \u003c/div\u003e\n\n}\n```\n\n### Import Angular in main.scala.html\n\nWe need to import angularjs in our application and create angular application using `ng-app`\n\n```html\n@(title: String)(content: Html)\n\n\u003c!DOCTYPE html\u003e\n\n\u003c!-- please note the directive ng-app to initialize angular app--\u003e\n\u003chtml ng-app=\"app\"\u003e\n    \u003chead\u003e\n        \u003ctitle\u003e@title\u003c/title\u003e\n        \u003clink rel=\"stylesheet\" media=\"screen\" href=\"@routes.Assets.at(\"stylesheets/main.css\")\"\u003e\n        \u003clink rel=\"shortcut icon\" type=\"image/png\" href=\"@routes.Assets.at(\"images/favicon.png\")\"\u003e\n        \u003cscript src=\"@routes.Assets.at(\"javascripts/jquery-1.9.0.min.js\")\" type=\"text/javascript\"\u003e\u003c/script\u003e\n        \u003cscript src=\"https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js\"\u003e\u003c/script\u003e\n        \u003cscript src=\"https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular-resource.min.js\"\u003e\u003c/script\u003e\n\n        \u003cscript src=\"@routes.Assets.at(\"javascripts/persons.js\")\" type=\"text/javascript\"\u003e\u003c/script\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n        @content\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\n## What else??? Oh yes Security...\n\n\u003eI know what you think: \"Uhuh, the poor guy who exposes his DB directly on the network and who is able to delete everything without any security\"\n\nOnce again, you're right. _(yes I know I love flattery)_\n\nAutosource is by default not secured in any way and actually I don't really care about security because this is your job to secure your exposed APIs and there are so many ways to secure services that I prefer to let you choose the one you want.\n\nAnyway, I'm a nice boy and I'm going to show you how you could secure the `DELETE` endpoint using the authentication action composition sample given in [Play Framework documentation](http://www.playframework.com/documentation/2.1.1/ScalaActionsComposition).\n\n```scala\nimport play.api.libs.iteratee.Done\nimport reactivemongo.bson.BSONObjectID\n\n// FAKE USER class to simulate a user extracted from DB.\ncase class User(name: String)\nobject User {\n  def find(name: String) = Some(User(name))\n}\n\nobject Persons extends ReactiveMongoAutoSourceController[Person] {\n  // The action composite directly copied for PlayFramework doc\n  def Authenticated(action: User =\u003e EssentialAction): EssentialAction = {\n    // Let's define a helper function to retrieve a User\n    def getUser(request: RequestHeader): Option[User] = {\n      request.session.get(\"user\").flatMap(u =\u003e User.find(u))\n    }\n\n    // Now let's define the new Action\n    EssentialAction { request =\u003e\n      getUser(request).map(u =\u003e action(u)(request)).getOrElse {\n        Done(Unauthorized)\n      }\n    }\n  }\n\n  val coll = db.collection[JSONCollection](\"persons\")\n\n  // \u003e\u003e\u003e IMPORTANT PART \u003c\u003c\u003c\n  // We simply override the delete action\n  // If authenticated, we call the original action\n  override def delete(id: BSONObjectID) = Authenticated { _ =\u003e\n    super.delete(id)\n  }\n\n  def index = Action {\n    Ok(views.html.index(\"ok\"))\n  }\n\n  // the login action which log any user\n  def login(name: String) = Action {\n    Ok(\"logged in\").withSession(\"user\" -\u003e name)\n  }\n\n  // the logout action which log out any user\n  def logout = Action {\n    Ok(\"logged out\").withNewSession\n  }\n}\n```\n\nNow, you can add routes to handle login and logout actions\n```scala\nPOST    /login/:name                controllers.Persons.login(name: String)\nPOST    /logout                     controllers.Persons.logout\n```\n\nNothing to complicated here.\nIf you need to add headers in your responses and params to querystring, it's easy to wrap autosource actions. Please refer to Play Framework doc for more info...\n\n\u003e I won't try it here, the article is already too long but it should work...\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n## Play-Autosource is DB agnostic\n\n`Play-Autosource` Core is independent of the DB and provides Reactive (Async/Nonblocking) APIs to fulfill PlayFramework requirements.\n\nNaturally this 1st implementation uses [ReactiveMongo](http://www.reactivemongo.org) which is one of the best sample of DB reactive driver. MongoDB fits very well in this concept too because document records are really compliant to JSON datasources.\n\nBut other implementations for other DB can be done and I count on you people to contribute them.\n\n\u003eDB implementation contributions are welcome (Play-Autosource is just _Apache2 licensed_) and AutoSource API are subject to evolutions if they appear to be erroneous.\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n## Conclusion\n\nPlay-Autosource provides a very fast \u0026 lightweight way to create a REST CRUD typesafe datasource in your Play/Scala application. You can begin with blob data such as `JsObject` and then elaborate the model of your data progressively by adding constraints or types to it.\n\nThere would be many more things to say about `Play/Autosource`:\n\n- you can also override writers to change output format\n- you have some alpha streaming API also\n- etc...\n\nThere are also lots of features to improve/add because it's still a very draft module.\n\n\nIf you like it and have ideas, don't hesitate to discuss, to contribute, to improve etc...\n\n`curl -X POST -d '{ \"coding\" : \"Have fun\"}' http://localhost:9000/developer`\n\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n\u003cbr/\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmandubian%2Fplay-autosource","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmandubian%2Fplay-autosource","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmandubian%2Fplay-autosource/lists"}