{"id":15040918,"url":"https://github.com/tambapps/garcon","last_synced_at":"2026-01-06T12:50:35.234Z","repository":{"id":50689140,"uuid":"517379396","full_name":"tambapps/garcon","owner":"tambapps","description":"lightweight HTTP server library with no dependencies for Marcel and Groovy","archived":false,"fork":false,"pushed_at":"2024-03-10T20:19:01.000Z","size":405,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-01T14:15:32.424Z","etag":null,"topics":["android","groovy","http-server","java"],"latest_commit_sha":null,"homepage":"","language":"Java","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/tambapps.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}},"created_at":"2022-07-24T16:38:08.000Z","updated_at":"2024-03-10T19:16:14.000Z","dependencies_parsed_at":"2023-12-23T22:18:30.926Z","dependency_job_id":"c7231901-62b2-4930-b660-b4dfbe5c12c2","html_url":"https://github.com/tambapps/garcon","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tambapps%2Fgarcon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tambapps%2Fgarcon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tambapps%2Fgarcon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tambapps%2Fgarcon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tambapps","download_url":"https://codeload.github.com/tambapps/garcon/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245814748,"owners_count":20676808,"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":["android","groovy","http-server","java"],"created_at":"2024-09-24T20:45:16.999Z","updated_at":"2026-01-06T12:50:35.195Z","avatar_url":"https://github.com/tambapps.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Garçon - lightweight HTTP Server library for Groovy and Marcel\n\nGarçon (pronounced `gar·son`, as in [garçon de café](https://en.wiktionary.org/wiki/gar%C3%A7on_de_caf%C3%A9)) is a lightweight HTTP Server library for Groovy and [Marcel](https://tambapps.github.io/marcel/), my own programming language.\n\nIts goal is to implement HTTP server the quickest and clearest way possible.\n\nThis library is hosted on Maven Central.\n- [garcon-groovy](https://central.sonatype.com/artifact/com.tambapps.http/garcon-groovy/2.0).\n- [garcon-marcel](https://central.sonatype.com/artifact/com.tambapps.http/garcon-marcel/2.0).\n\n## Implement Simple CRUD API\n\nYou can implement a simple todos API (based on [JSONPlaceholder API](https://jsonplaceholder.typicode.com/)) with a script\n\n### In Groovy\n\n```groovy\n@Grab('com.tambapps.http:garcon:1.1-SNAPSHOT')\nimport com.tambapps.http.garcon.*\nimport com.tambapps.http.garcon.annotation.*\nimport com.tambapps.http.garcon.exception.*\nimport groovy.transform.Field\n\nclass Todo {\n  Integer userId\n  Integer id\n  String title\n  Boolean completed\n}\n\n@Field\nfinal Map todosById = [\n    new Todo(id: 1, userId: 1, title: \"delectus aut autem\", completed: false),\n    new Todo(id: 2, userId: 1, title: \"quis ut nam facilis et officia qui\", completed: false),\n    new Todo(id: 3, userId: 1, title: \"fugiat veniam minus\", completed: false),\n    new Todo(id: 4, userId: 1, title: \"et porro tempora\", completed: true)\n].collectEntries { [it.id, it] }\n\n@ResponseStatus(HttpStatus.CREATED)\n@Post('/todos')\npostTodo(@ParsedRequestBody Todo post) {\n  if (!post.userId || !post.title || post.completed == null) {\n    throw new BadRequestException(\"Some fields are missing/malformed\")\n  }\n  post.id = todosById.size() + 1\n  todosById[post.id] = post\n  return post\n}\n\n@Get('/todos')\ngetTodos() {\n  return todosById.values().sort { it.id }\n}\n\n@Get('/todo/{id}')\ngetTodo(@PathVariable(\"id\") Integer id) {\n  def todo = todosById[id]\n  if (todo) return todo\n  throw new NotFoundException(\"Todo was not found\")\n}\n\n@Patch('/todo/{id}')\npatchTodo(@PathVariable(\"id\") Integer id, @ParsedRequestBody Todo patch) {\n  def todo = getTodo(id)\n  if (patch.userId) todo.userId = patch.userId\n  if (patch.title) todo.title = patch.title\n  if (patch.completed != null) todo.completed = patch.completed\n  return todo\n}\n\n@Delete('/todo/{id}')\ndeleteTodo(@PathVariable(\"id\") Integer id) {\n  def todo = getTodo(id)\n  todosById.remove(id)\n  return todo\n}\n\nvoid onStart(InetAddress address, int port) {\n  println \"Started on $address:$port\"\n}\n\nGarcon.fromInstance(this, accept: ContentType.JSON, contentType: ContentType.JSON)\n    .start(address: \"localhost\", port: 8081)\n```\n\n## Define your endpoints dynamically\n\nYou can also define endpoints dynamically using `Garcon.serve(Closure)` method\n\n````groovy\ndef garcon = new Garcon(InetAddress.getByName(\"localhost\"), 8081)\ngarcon.serve {\n  get 'hello/{someone}', {\n    return \"Hello $someone\"\n  }\n  get '/hello', contentType: ContentType.JSON, {\n    return [hello: 'world']\n  }\n  post '/path', accept: ContentType.JSON, {\n    return \"Hello ${parsedRequestBody.who}\"\n  }\n}\n\ngarcon.join()\n````\n\n### In Marcel\n\nI created [Marcel](https://tambapps.github.io/marcel/), my own programming language.\n\n```kotlin\ndumbbell 'com.tambapps.http:garcon-marcel:2.0-SNAPSHOT'\n\n/**\n * imports\n **/\nimport com.tambapps.http.garcon.*\nimport com.tambapps.http.garcon.annotation.*\nimport com.tambapps.http.garcon.exception.*\nimport java.net.InetAddress\nimport java.util.concurrent.atomic.AtomicInteger\n\n/**\n * Data\n **/\nstatic final AtomicInteger ID_INCREMENT  = new AtomicInteger()\nstatic final List TODOS = [\n  new Todo(id: ID_INCREMENT.incrementAndGet(), userId: 1, title: \"Go to the grocery store\", completed: false),\n  new Todo(id: ID_INCREMENT.incrementAndGet(), userId: 1, title: \"Finish homeworks\", completed: false),\n  new Todo(id: ID_INCREMENT.incrementAndGet(), userId: 2, title: \"Do the dishes\", completed: false),\n  new Todo(id: ID_INCREMENT.incrementAndGet(), userId: 3, title: \"Charge computer\", completed: true)\n]\n\nclass Todo {\n  Integer id\n  Integer userId\n  String title\n  Boolean completed\n\n  constructor(this.id, this.userId, this.title, this.completed)\n}\n\n/**\n * API\n **/\n@Get('/todos')\nfun Collection getTodos() {\n  return TODOS\n}\n\n@Get('/todos/{id}')\nfun Object getTodo(@PathVariable(\"id\") Integer id) {\n  Todo todo = TODOS.find { Todo it -\u003e it.id == id }\n  if (todo) return todo\n  throw new NotFoundException(\"Todo with id $id not found\")\n}\n\n@ResponseStatus(CREATED)\n@Post('/todos')\nfun Object postTodo(@ParsedRequestBody dynobj post) {\n  if (!post.userId || !post.title) {\n    throw new BadRequestException(\"Some fields are missing/malformed\")\n  }\n  TODOS.add(\n    new Todo(id: ID_INCREMENT.incrementAndGet(), userId: post.userId.asInt(), title: post.title.asString(), completed: false)\n  )\n  return post\n}\n\n@Patch('/todos/{id}')\nfun Object patchTodo(@PathVariable(\"id\") Integer id, @ParsedRequestBody dynobj patch) {\n  Todo todo = getTodo(id)\n  if (patch.userId != null) todo.userId = patch.userId.asInt()\n  if (patch.title != null) todo.title = patch.title.asString()\n  if (patch.completed != null) todo.completed = patch.completed.asBool()\n  return todo\n}\n\n@Delete('/todos/{id}')\nfun Object deleteTodo(@PathVariable(\"id\") Integer id) {\n  Todo todo = TODOS.find { Todo it -\u003e it.id == id }\n  TODOS.remove(todo)\n  return todo\n}\n\n\nfun void onStart(InetAddress address, int port) {\n  println(\"Started on ${address.hostName}:$port\")\n}\n\nGarcon.fromInstance(this, accept: ContentType.JSON, contentType: ContentType.JSON)\n    .start(address: \"localhost\", port: 8081)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftambapps%2Fgarcon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftambapps%2Fgarcon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftambapps%2Fgarcon/lists"}