{"id":13753851,"url":"https://github.com/daviddenton/fintrospect","last_synced_at":"2025-04-14T17:11:10.662Z","repository":{"id":28014953,"uuid":"31509675","full_name":"daviddenton/fintrospect","owner":"daviddenton","description":"Implement fast, type-safe HTTP webservices for Finagle","archived":false,"fork":false,"pushed_at":"2020-08-26T11:42:43.000Z","size":15810,"stargazers_count":90,"open_issues_count":3,"forks_count":10,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-28T05:51:03.332Z","etag":null,"topics":["finagle","framework","http","microservice","performance","scala","techempower"],"latest_commit_sha":null,"homepage":"http://fintrospect.io","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/daviddenton.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-03-01T20:31:11.000Z","updated_at":"2024-03-17T23:00:09.000Z","dependencies_parsed_at":"2022-07-22T19:03:03.215Z","dependency_job_id":null,"html_url":"https://github.com/daviddenton/fintrospect","commit_stats":null,"previous_names":[],"tags_count":180,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviddenton%2Ffintrospect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviddenton%2Ffintrospect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviddenton%2Ffintrospect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviddenton%2Ffintrospect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daviddenton","download_url":"https://codeload.github.com/daviddenton/fintrospect/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248923764,"owners_count":21183954,"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":["finagle","framework","http","microservice","performance","scala","techempower"],"created_at":"2024-08-03T09:01:31.372Z","updated_at":"2025-04-14T17:11:10.634Z","avatar_url":"https://github.com/daviddenton.png","language":"Scala","funding_links":[],"categories":["scala"],"sub_categories":[],"readme":"\u003ch1\u003e\n\u003ca href=\"http://fintrospect.io\"\u003eFintrospect\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\u003ca href=\"https://bintray.com/fintrospect/maven/fintrospect-core/_latestVersion\"\u003e\u003cimg src=\"https://api.bintray.com/packages/fintrospect/maven/fintrospect-core/images/download.svg\"/\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\u003ca href=\"https://travis-ci.org/daviddenton/fintrospect\"\u003e\u003cimg src=\"https://travis-ci.org/daviddenton/fintrospect.svg?branch=master\"/\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\u003ca href=\"https://coveralls.io/github/daviddenton/fintrospect?branch=master\"\u003e\u003cimg src=\"https://coveralls.io/repos/daviddenton/fintrospect/badge.svg?branch=master\"/\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\u003ca href=\"https://gitter.im/daviddenton/fintrospect\"\u003e\u003cimg src=\"https://badges.gitter.im/daviddenton/fintrospect.svg\"/\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\u003ca href=\"https://bintray.com/daviddenton/maven/fintrospect/view?source=watch\"\u003e\u003cimg height=\"45\" src=\"https://www.bintray.com/docs/images/bintray_badge_color.png\"/\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u0026nbsp;\n\u003c/h1\u003e\n\nFintrospect is a Scala web-framework with an intelligent HTTP routing layer, based on the \n\u003ca href=\"http://twitter.github.io/finagle/\"\u003eFinagle\u003c/a\u003e RPC framework from Twitter. Via a shared contract, it provides a simple way to \nimplement fast webservice endpoints and HTTP clients which are:\n\n- ```Type-safe``` : auto-marshals request parameters/bodies into the correct primitive and custom types.\n- ```Auto-validating``` : enforce the correctness of required/optional request parameters/bodies, generating a BadRequest if the contract is broken.\n- ```Auto-documenting``` : runtime generation of endpoint documentation such as \u003ca href=\"http://swagger.io/\"\u003eSwagger\u003c/a\u003e JSON or web sitemap XML. \nGenerates \u003ca href=\"http://json-schema.org/\"\u003eJSON Schema\u003c/a\u003e for example object formats to be included in these API docs.\n- ```Uniform``` : reuse the same contract to define both server endpoints and HTTP clients This also allows extremely low effort fake servers to be created\n\nAdditionally, Fintrospect provides a number of mechanisms to leverage these routes:\n\n- Easily build type-safe HTTP responses with a set of custom builders for a wide variety of message formats:\n  - JSON: \u003ca href=\"http://argo.sourceforge.net/\"\u003eArgo\u003c/a\u003e, \u003ca href=\"http://argonaut.io/\"\u003eArgonaut\u003c/a\u003e, \n  \u003ca href=\"https://github.com/travisbrown/circe\"\u003eCirce\u003c/a\u003e, \u003ca href=\"https://github.com/google/gson\"\u003eGSON\u003c/a\u003e, \n  \u003ca href=\"https://github.com/FasterXML/jackson\"\u003eJackson\u003c/a\u003e, \u003ca href=\"http://json4s.org/\"\u003eJson4S\u003c/a\u003e, \u003ca href=\"https://github.com/playframework\"\u003ePlay JSON\u003c/a\u003e, \n  \u003ca href=\"https://github.com/spray/spray-json\"\u003eSpray JSON\u003c/a\u003e\n    - Auto-marshaling of case classes instances to/from JSON (for `Argonaut`/`Circe`/`Json4S`/`Play`).\n    - Implement simple `PATCH`/`PUT` endpoints of case class instances (`Circe` only).\n  - Native implementations of XML, Plain Text, HTML, XHTML\n  - \u003ca href=\"http://msgpack.org\"\u003eMsgPack\u003c/a\u003e binary format\n- Serve static content from the classpath or a directory\n- Template ```View``` support (with Hot-Reloading) for building responses with \u003ca href=\"http://mustache.github.io/\"\u003eMustache\u003c/a\u003e or \u003ca href=\"http://handlebarsjs.com\"\u003eHandlebars\u003c/a\u003e\n- Anonymising headers for dynamic-path based endpoints, removing all dynamic path elements. This allows, for example, calls to particular endpoints to be grouped for metric purposes. e.g. \n```/search/author/rowling``` becomes ```/search/author/{name}```\n- Interacts seamlessly with other Finagle based libraries, such as \u003ca href=\"https://github.com/finagle/finagle-oauth2\"\u003eFinagle OAuth2\u003c/a\u003e \n- Utilities to help you unit-test endpoint services and write HTTP contract tests for remote dependencies \n\n## Get it\nFintrospect is intentionally dependency-lite by design - other than Finagle, the core library itself only has a single non `org.scala` dependency. \nNo dependency on `Scalaz`, `Cats` or `Shapeless`, so there are no compatibility headaches.\n\nTo activate the extension library features (JSON, templates etc), additional dependencies are required - please see \u003ca href=\"http://fintrospect.io/installation\"\u003ehere\u003c/a\u003e for details.\n\nAdd the following lines to ```build.sbt``` - the lib is hosted in Maven Central and JCenter:\n```scala\nresolvers += \"JCenter\" at \"https://jcenter.bintray.com\"\nlibraryDependencies += \"io.fintrospect\" %% \"fintrospect-core\" % \"17.0.0\"\n```\n\n## See the code\nSee the \n\u003ca href=\"https://github.com/daviddenton/fintrospect/tree/master/src/main/scala/examples\"\u003eexamples\u003c/a\u003e or \n\u003ca href=\"https://github.com/daviddenton/fintrospect/tree/master/src/test/scala/cookbook\"\u003ecookbook\u003c/a\u003e in this repo, or clone the \n\u003ca href=\"http://github.com/daviddenton/fintrospect-example-app\"\u003efull example application repo\u003c/a\u003e.\n\n## Learn it\nSee the full user guide \u003ca href=\"http://fintrospect.io/\"\u003ehere\u003c/a\u003e, or read on for the tldr; example. :)\n\n### Server-side contracts\nAdding Fintrospect routes to a Finagle HTTP server is simple. For this example, we'll imagine a Library application (see the example \nabove for the full code) which will be rendering Swagger v2 documentation.\n\n#### Define the endpoint\nThis example is quite contrived (and almost all the code is optional) but shows the kind of thing that can be done. Note the use of the \nexample response object, which will be broken down to provide the JSON model for the Swagger documentation. \n\n```scala\n// Response building methods and implicit conversion from ResponseBuilder -\u003e Future[Response] pulled in here\nimport io.fintrospect.formats.Argo.ResponseBuilder._\nimport io.fintrospect.formats.Argo.JsonFormat.array\n\nclass BookSearch(books: Books) {\n  private val maxPages = Query.optional.int(\"maxPages\", \"max number of pages in book\")\n  private val minPages = FormField.optional.int(\"minPages\", \"min number of pages in book\")\n  private val titleTerm = FormField.required.string(\"term\", \"the part of the title to look for\")\n  private val form = Body.form(minPages, titleTerm)\n\n  private def search() = Service.mk[Request, Response] { \n    request =\u003e {\n      val requestForm = form \u003c-- request\n      Ok(array(\n        books.search(\n            (minPages \u003c-- requestForm).getOrElse(MIN_VALUE), \n            (maxPages \u003c-- request).getOrElse(MAX_VALUE),\n            titleTerm \u003c-- requestForm\n        ).map(_.toJson)))\n    }\n  }\n\n  val route = RouteSpec(\"search for books\")\n    .taking(maxPages)\n    .body(form)\n    .returning(Status.Ok -\u003e \"we found your book\", array(Book(\"a book\", \"authorName\", 99).toJson))\n    .returning(Status.BadRequest -\u003e \"invalid request\")\n    .producing(ContentTypes.APPLICATION_JSON)\n    .at(Method.Post) / \"search\" bindTo search\n}\n```\n\n#### Define a module to live at ```http://{host}:8080/library```\nThis module will have a single endpoint ```search```:\n\n```scala\nval apiInfo = ApiInfo(\"Library Example\", \"1.0\", Option(\"Simple description\"))\nval renderer = Swagger2dot0Json(apiInfo) \nval libraryModule = RouteModule(Root / \"library\", renderer)\n    .withRoute(new BookSearch(new BookRepo()).route)\nHttp.serve(\":8080\", new HttpFilter(Cors.UnsafePermissivePolicy).andThen(libraryModule.toService)) \n```\n\n#### View the generated documentation\nThe auto-generated documentation lives at the root of the module, so point the Swagger UI at ```http://{host}:8080/library``` to see it.\n\n### Client-side contracts\nDeclare the fields to be sent to the client service and then bind them to a remote service. This produces a simple function, which can \nthen be called with the bindings for each parameter.\n\nSince we can re-use the routes between client and server, we can easily create fake implementations of remote systems without having to \nredefine the contract. This means that marshalling of objects and values into/out of the HTTP messages can be reused.\n```scala\n  val theDate = Path.localDate(\"date\")\n  val gender = FormField.optional.string(\"gender\")\n  val body = Body.form(gender)\n\n  val sharedRouteSpec = RouteSpec()\n    .body(body)\n    .at(Get) / \"firstSection\" / theDate\n\n  val fakeServerRoute = sharedRouteSpec bindTo (dateFromPath =\u003e Service.mk[Request, Response] {\n    request: Request =\u003e {\n      // insert stub server implementation in here\n      println(\"Form sent was \" + (body \u003c-- request))\n      Ok(dateFromPath.toString)\n    }\n  })\n\n  Await.result(new TestHttpServer(10000, fakeServerRoute).start())\n\n  val client = sharedRouteSpec bindToClient Http.newService(\"localhost:10000\")\n\n  val theCall = client(\n    body --\u003e Form(gender --\u003e \"male\"), \n    theDate --\u003e LocalDate.of(2015, 1, 1)\n  )\n\n  println(Await.result(theCall))\n```\n\n## Upgrading?\nSee the \u003ca href=\"https://github.com/daviddenton/fintrospect/blob/master/CHANGELOG.md\"\u003echangelog\u003c/a\u003e.\n\n## Contributing\nThere are many ways in which you can contribute to the development of the library:\n\n- Give us a ⭐️ on Github - you know you want to ;)\n- Questions can be directed towards the Gitter channel, or on Twitter \u003ca href=\"https://twitter.com/fintrospectdev\"\u003e@fintrospectdev\u003c/a\u003e\n- For issues, please describe giving as much detail as you can - including version and steps to recreate\n\nSee the \u003ca href=\"https://github.com/daviddenton/fintrospect/blob/master/CONTRIBUTING.md\"/\u003econtributor guide\u003c/a\u003e for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaviddenton%2Ffintrospect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaviddenton%2Ffintrospect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaviddenton%2Ffintrospect/lists"}