{"id":45157430,"url":"https://github.com/couchbase-examples/scala-quickstart","last_synced_at":"2026-02-20T05:02:44.820Z","repository":{"id":41585815,"uuid":"446440130","full_name":"couchbase-examples/scala-quickstart","owner":"couchbase-examples","description":"Entry level Couchbase Scala tutorial/demo. Steps to build REST APIs to manage user profile CRUD operations.","archived":false,"fork":false,"pushed_at":"2025-02-12T16:08:34.000Z","size":81,"stargazers_count":4,"open_issues_count":1,"forks_count":2,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-02-12T17:24:27.906Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/couchbase-examples.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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-01-10T13:44:50.000Z","updated_at":"2025-02-12T16:08:33.000Z","dependencies_parsed_at":"2024-05-09T03:42:59.481Z","dependency_job_id":"bbad230a-8970-413a-870b-113573b71dd9","html_url":"https://github.com/couchbase-examples/scala-quickstart","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/couchbase-examples/scala-quickstart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/couchbase-examples%2Fscala-quickstart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/couchbase-examples%2Fscala-quickstart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/couchbase-examples%2Fscala-quickstart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/couchbase-examples%2Fscala-quickstart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/couchbase-examples","download_url":"https://codeload.github.com/couchbase-examples/scala-quickstart/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/couchbase-examples%2Fscala-quickstart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29641929,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T03:21:14.183Z","status":"ssl_error","status_checked_at":"2026-02-20T03:18:24.455Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":"2026-02-20T05:02:39.333Z","updated_at":"2026-02-20T05:02:44.805Z","avatar_url":"https://github.com/couchbase-examples.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Quickstart in Couchbase with Scala\n\n#### Build REST APIs with Couchbase's Scala SDK\n\n\u003e This repo is designed to teach you how to connect to a Couchbase cluster to create, read, update, and delete documents and how to write simple parametrized SQL++ queries.\n\nFull documentation can be found on the [Couchbase Developer Portal](https://developer.couchbase.com/tutorial-quickstart-scala-webservers).\n\n## Prerequisites\n\nTo run this prebuilt project, you will need:\n\n- [Couchbase Capella](https://www.couchbase.com/products/capella/) cluster with [travel-sample](https://docs.couchbase.com/php-sdk/current/ref/travel-app-data-model.html) bucket loaded.\n    - To run this tutorial using a self-managed Couchbase cluster, please refer to the [appendix](#running-self-managed-couchbase-cluster).\n- Scala 2, version 2.13.9 or higher installed with [supported by it JDK](https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html)\n- Loading Travel Sample Bucket\n    If travel-sample is not loaded in your Capella cluster, you can load it by following the instructions for your Capella Cluster:\n    - [Load travel-sample bucket in Couchbase Capella](https://docs.couchbase.com/cloud/clusters/data-service/import-data-documents.html#import-sample-data)\n\n## Source Code\n\n```shell\ngit clone https://github.com/couchbase-examples/scala-quickstart.git\n```\n\n### Database and Web Server Configuration\n\nAll configuration for communication with the database and the three web servers is stored in the `/src/main/resources/application.conf` file.  This includes the connection string, username, and password for Couchbase, and the port number for the API server.\n\nThe default username for Couchbase server is assumed to be `Administrator` and the default password is assumed to be `password`.  If these are different in your environment you will need to change them before running the application.\n\n### Couchbase Capella Specific Configuration\n\nThis section is only needed when you've opted for using a Couchbase Capella cloud instance for following the tutorial.\n\nTo enable the tutorial code to establish a connection to Capella, you will need to make a couple of changes:\n- import `travel-sample` sample bucket with `airline` collection onto your Capella cluster\n- make sure your `username` in `application.conf` is [configured for access](https://docs.couchbase.com/cloud/get-started/configure-cluster-access.html) to this bucket\n- change the `host` in `application.conf` to the **Public Connection String** address in Capella -\u003e Databases -\u003e `your_database_name` -\u003e Connect. See the image below:\n  ![capella_host_address](https://developer.couchbase.com/static/5b77fd7cee748503423638f249b98261/38697/connect_wan.png)\n\nPossible issues:\n- The Scala SDK does not support a bucket path as part of the `host` name in `application.conf`, use `bucket-name` instead\n- It is not supported to create a bucket in Capella through the SDK. If you get a `Error: unable to find bucket \u003cbucket-name\u003e`,\n  please make sure you've created the bucket through the Capella user interface, and the name lines up with `bucket-name` in `application.conf`\n\n## Running The Application\n\nAt this point the application is ready, and you can run it via your IDE or from the terminal:\n\n```shell\nsbt run\n```\n\n\u003e Note: If using multiple JDK installations, you will need to provide the path to the proper JDK home folder using `--jdk-home` argument to `sbt` command. This tutorial was tested to work with JDK17.\n\n\u003e Note: When using Option 2: Couchbase Server, then Couchbase Server 7 must be installed and running on localhost (http://127.0.0.1:8091) prior to running the Scala application.\n\nThe application will keep running until you provide a line of input, after which it will shut down the web servers.\n\nYou can launch your browser and go to API server's [Swagger start page](http://localhost:8080/docs/).\n\n## What We'll Cover\n\nSimple REST APIs using the Scala Couchbase SDK with the following Airline endpoints as example:\n\n- [POST an airline](#post-an-airline) – Create a new airline record\n- [GET an airline by Key](#get-an-airline-by-key) – Get a specific airline\n- [PUT airline](#put-airline) – Update an airline\n- [DELETE airline](#delete-airline) – Delete an airline\n- [GET airlines by searching](#get-airlines-by-searching)  – Get all airlines matching provided name or code\n\n## Document Structure\n\nWe will be hosting up REST APIs using embedded Netty webserver. Endpoint descriptions and [Swagger documentation](https://swagger.io/) is created through the [tapir framework](https://tapir.softwaremill.com/).\n\nThe REST APIs will be used to manage airline records. Our airline document will have an auto-generated UUID for its key,\nname of the airline as well as its country, callsign and both IATA and ICAO codes. For this demo we will store all airline information in\njust one document in a collection named `airline`:\n\n```json\n{\n  \"id\": \"b181551f-071a-4539-96a5-8a3fe8717faf\",\n  \"name\": \"CouchAir\",\n  \"country\": \"zz\",\n  \"callsign\": \"BASE\",\n  \"iata\": \"CBA\",\n  \"icao\": \"CBAR\"\n}\n```\n\n## Let's Review the Code\n\nTo begin clone the repo and open it up in the IDE of your choice to learn about how to create, read, update and delete documents in your Couchbase Server.\n\n## POST an Airline\n\nFor CRUD operations we will use the [Key Value operations](https://docs.couchbase.com/scala-sdk/current/howtos/kv-operations.html) that are built into the Couchbase SDK to create, read, update, and delete a document. Every document will need an ID (similar to a primary key in other databases) in order to save it to the database.\n\nIf we look at the the `AirlineController` trait, found in the controllers folder and navigate to the postAirline function, then we can see the following type signature:\n\n```scala\ndef postAirline(airlineInput: AirlineInput): F[Either[String, Airline]]\n```\n\nThe abstract definitions in `AirlineController` (and `CouchbaseConnection`) are generalized over some effect `F`, making it easier to switch between implementations such as `Future` and `IO`. This will help in defining our different REST APIs and also make it easier to test our code.\n\nWithin the `F` type parameter, we can see that we return an `Either[String, Airline]]` representing an error string, or a successful result with the `Airline` case class.\n\nThe input for posting an `Airline` is an `AirlineInput`:\n\n```scala\nfinal case class AirlineInput(\n                                     name: String,\n                                     country: String,\n                                     callsign: String,\n                                     iata: String,\n                                     icao: String\n                             )\n```\n\nThe implementation for `postAirline` in `CouchbaseAirlineController` will transform the input object into airline model instance:\n```scala\nfinal case class Airline(\n                                id: Int32,\n                                name: String,\n                                country: String,\n                                callsign: String,\n                                iata: String,\n                                icao: String\n                        )\n}\n```\n\nOur `Airline` document is ready to be persisted to the database.  We create call to the `collection` using the local variable `airlineCollection: Future[Collection]` and then call the `insert` method  and passing it the UUID from the `Airline` as the key.\n\nNote that the [Scala SDK has support for various types of JSON](https://docs.couchbase.com/scala-sdk/current/howtos/json.html) commonly used within the Scala community. Here we use the [circe](https://circe.github.io/circe/) JSON library to convert the `Airline` with `.asJson` and insert it.\n\nOnce the document is inserted we then return the document saved and the result all as part of the same object back to the user.\n\n```scala\noverride def post(airlineInput: AirlineInput): Future[Either[String, Airline]] = {\n  import io.circe.syntax._\n  for {\n    ac \u003c- airlineCollection\n    airline \u003c- Airline.fromAirlineInput(airlineInput) match {\n      case Failure(exception) =\u003e Future.successful(Left(exception.toString))\n      case Success(a) =\u003e\n        ac.insert[io.circe.Json](a.id.toString, a.asJson) map (_ =\u003e Right(a))\n    }\n  } yield airline\n}\n```\n\u003e *from controllers/CouchbaseAirlineController.scala*\n\n## GET an Airline by Key\n\nNavigate to the `getAirline` function in the `CouchbaseAirlineController` file in the controllers folder.  We only need\nthe airline `id` from the user to retrieve a particular airline document using a basic key-value operation which is\npassed in the method signature as a string. Since we created the document with a unique key we can use that key to find\nthe document in the scope and collection it is stored in.\n\n```scala\noverride def get(id: UUID): Future[Either[String, Airline]] = {\n  for {\n    ac \u003c- airlineCollection\n    res \u003c- ac.get(id.toString).map(_.contentAsCirceJson[Airline]).\n            recover { case _: DocumentNotFoundException =\u003e Left(s\"Could not retrieve Airline. ID: $id was not found.\")}\n  } yield res\n}\n```\n\n\u003e *from getAirline function in controllers/CouchbaseAirlineController.scala and using the implicit class in models/CirceGetResult.*\n\n\n## PUT Airline\n\nNow let's navigate to the `putAirline` function of the `CouchbaseAirlineController` class. The entire document gets\nreplaced except for the document key and the `id` field.  We create a call to the `collection` using the `upsert` method\nand then return the document saved and the result just as we did in the previous endpoint.\n\nThe only difference in implementation with `postAirline` is the following line:\n```scala\nac.upsert[io.circe.Json](args.id.toString, p.asJson)\n```\n\n\u003e *from update method of controllers/CouchbaseAirlineController.scala*\n\n## DELETE Airline\n\nNavigate to the `deleteAirline` function in the `CouchbaseAirlineController` class. We only need the `Key` or `id` from\nthe user to remove a document using a basic key-value operation.\n\n```scala\nac.remove(id.toString)\n```\n\n\u003e *from deleteAirline method of controllers/CouchbaseAirlineController.scala*\n\n## GET Airlines by Searching\n\n[SQL++ (N1QL)](https://docs.couchbase.com/scala-sdk/current/howtos/n1ql-queries-with-sdk.html) is a powerful query language based on SQL, but designed for structured and flexible JSON documents.\nWe will use a SQL++ query to search for profiles with Skip, Limit, and Search options.\n\nNavigate to the `getAirlines` method in the `CouchbaseAirlineController` class. This endpoint is different from all\nthe others because it makes the SQL++ query rather than a key-value operation. This means more overhead because the\nquery engine is involved. You may need to create an [index](https://docs.couchbase.com/server/current/learn/services-and-indexes/indexes/indexing-and-query-perf.html)\nspecific for this query on fields `name`, `country`, `callsign`, `icao`, `iata`, so it would be performant.\n\nThe individual `skip` (optional), `limit` (optional), and `search` values are obtained from their respective parameters.\nThen, we build our SQL++ query using the parameters that were passed in.\n\nFinally, we pass that `query` to the `cluster.query` method and return the result.\n\nTake notice of the SQL++ syntax and how it targets the `bucket`.`scope`.`collection`.\n\n```scala\n  override def list(args: ListingInput): Future[Either[String, List[Airline]]] = {\n  val query = s\"SELECT p.* FROM \" +\n          s\"`${quickstartConfig.couchbase.bucketName}`.`${quickstartConfig.couchbase.scopeName}`.`${quickstartConfig.couchbase.collectionName}` a \" +\n          s\"WHERE lower(a.name) LIKE '%${args.search.toLowerCase}%' \" +\n          s\"OR lower(a.country) LIKE '%${args.search.toLowerCase}%'  \" +\n          s\"OR lower(a.callsign) LIKE '%${args.search.toLowerCase}%'  \" +\n          s\"OR lower(a.iata) LIKE '%${args.search.toLowerCase}%'  \" +\n          s\"OR lower(a.icao) LIKE '%${args.search.toLowerCase}%'  \" +\n          s\"LIMIT \" + args.limit.getOrElse(5) + \" OFFSET \" + args.skip.getOrElse(0)\n\n  import cats.implicits._\n  for {\n    cluster \u003c- couchbaseConnection.cluster\n    rows \u003c- Future.fromTry(\n      cluster.query(\n        query,\n        QueryOptions(scanConsistency =\n          Some(QueryScanConsistency.RequestPlus())\n        )\n      )\n    )\n    airlines \u003c- Future.fromTry(\n      rows\n              .rowsAs[io.circe.Json]\n              .map(_.map(json =\u003e json.as[Airline].left.map(_.getMessage())))\n    )\n    accumulatedAirlines = airlines.toList.sequence\n  } yield accumulatedAirlines\n}\n```\n\n\u003e *from getAirlines method of controllers/CouchbaseAirlineController.scala*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcouchbase-examples%2Fscala-quickstart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcouchbase-examples%2Fscala-quickstart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcouchbase-examples%2Fscala-quickstart/lists"}