Kitura tutorial part 2: CouchDB
In part one we set up Swift and build a “hello world” Kitura application, so we are well placed to build an API that actually does something. This API will manage a list of books. Clearly, we’ll need to store our books somewhere and I’ve chosen CouchDB for this tutorial.
To access CouchDB, you use HTTP which makes it very easy to work with curl or any other HTTP client. The docs are useful!
This part of the tutorial concentrates solely on setting up CouchDB, populating it and ensuring that we can operate with it.
The data
For this project we’re going to manage a list of books. Each book will have the following fields:
- Title
- Author
- ISBN
This isn’t a complicated database!
Install CouchDB
On OS X, you can install CouchDB using homebrew:
$ brew install couchdb
On Linux, install via apt:
$ sudo add-apt-repository ppa:couchdb/stable -y
$ sudo apt-get update
$ sudo apt-get install couchdb
You now have a local CouchDB installation on http://localhost:5984.
The next step is to set up an admin user:
$ curl -i -X PUT http://localhost:5984/_config/admins/{username} -d '"{password}"'
Select any username and password that you like, but don’t forget it! I’m going to use rob
/123456
throughout the rest of the tutorial. If the command succeeded, it returns an empty string.
Populate CouchDB with some data
To populate our database, we need some data, a design document and a bash script to do the work. We’ll create three files in a new directory called scripts
:
- scripts/books.json
- scripts/main_design.json
- scripts/seed_couchdb.sh
These are the files. Create them in scripts
.
scripts/books.json
{
"docs": [
{"type": "book", "title": "Gregor the Overlander", "author": "Suzanne Collins", "isbn": "9780439678131"},
{"type": "book", "title": "Gregor and the Prophecy of Bane", "author": "Suzanne Collins", "isbn": "9780439650762"},
{"type": "book", "title": "Gregor and the Curse of the Warmbloods", "author": "Suzanne Collins", "isbn": "9780439656245"},
{"type": "book", "title": "Gregor and the Marks of Secret", "author": "Suzanne Collins", "isbn": "9780439791465"},
{"type": "book", "title": "Gregor and the Code of Claw", "author": "Suzanne Collins", "isbn": "9780439791441"},
{"type": "book", "title": "The Hunger Games", "author": "Suzanne Collins", "isbn": "9780439023528"},
{"type": "book", "title": "Catching Fire", "author": "Suzanne Collins", "isbn": "9780545227247"},
{"type": "book", "title": "Mockingjay", "author": "Suzanne Collins", "isbn": "9780439023511"},
{"type": "book", "author": "Anne McCaffrey", "title": "Dragonflight", "isbn": "9780345335463"},
{"type": "book", "author": "Anne McCaffrey", "title": "Dragonquest", "isbn": "9780345022455"},
{"type": "book", "author": "Anne McCaffrey", "title": "The White Dragon", "isbn": "9780345275677"},
{"type": "book", "author": "Anne McCaffrey", "title": "Dragonsong", "isbn": "9780689305078"},
{"type": "book", "author": "Anne McCaffrey", "title": "Dragonsinger", "isbn": "9780689305702"},
{"type": "book", "author": "Anne McCaffrey", "title": "Dragondrums", "isbn": "9780689306853"},
{"type": "book", "author": "Peter F. Hamilton", "title": "Mindstar Rising", "isbn": "9780330537742"},
{"type": "book", "author": "Peter F. Hamilton", "title": "A Quantum Murder", "isbn": "9780330537759"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Nano Flower", "isbn": "9780330537810"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Reality Dysfunction", "isbn": "9781447208570"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Neutronium Alchemist", "isbn": "9781447208587"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Naked God", "isbn": "9781447208594"},
{"type": "book", "author": "Peter F. Hamilton", "title": "Misspent Youth", "isbn": "9781447224082"},
{"type": "book", "author": "Peter F. Hamilton", "title": "Pandora's Star", "isbn": "9781447279662"},
{"type": "book", "author": "Peter F. Hamilton", "title": "Judas Unchained", "isbn": "9781447279679"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Dreaming Void", "isbn": "9781447208563"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Temporal Void", "isbn": "9780330507882"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Evolutionary Void", "isbn": "9780330443173"},
{"type": "book", "author": "Peter F. Hamilton", "title": "The Abyss Beyond Dreams", "isbn": "9780230769465"}
]
}
This JSON file contains a ‘docs’ array containing a set of objects for each book in our database. As CouchDB is a document database, we don’t have tables are we would do in a standard RDBMS, so I’ve also chosen to have an additional property called type
which is set to book
. We don’t techncially need this, but it makes it easier to write our views as we can test for the type to ensure we have a book document.
scripts/main_design.json
{
"id": "_design/main_design",
"views": {
"all_books": {
"map" : "function(doc) { if (doc.type == 'book') { emit([doc.author, doc.title], doc); } }"
}
},
"language": "javascript"
}
The main design document contains our list of views. In this case we have a single view called all_books
which will emit all records of type “book”. In CouchDB, each view is a JavaScript function that outputs the documents that match a given criteria. For all_books
, we simply check that the type
is book
and if it is, emit the document using the compound key of author
and title
so that the list is ordered by author, then book title.
scripts/seed_couchdb.sh
#!/bin/bash -e
# This script is inspired from similar scripts in the Kitura BluePic project
# Find our current directory
current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# Parse input parameters
database=bookshelf_db
url=http://localhost:5984
for i in "$@"
do
case $i in
--username=*)
username="${i#*=}"
shift
;;
--password=*)
password="${i#*=}"
shift
;;
--url=*)
url="${i#*=}"
shift
;;
*)
;;
esac
done
if [ -z $username ]; then
echo "Usage:"
echo "seed_couchdb.sh --username= --password= [--url=]"
echo " default for --url is '$url'"
exit
fi
# delete and create database to ensure it's empty
curl -X DELETE $url/$database -u $username:$password
curl -X PUT $url/$database -u $username:$password
# Upload design document
curl -X PUT "$url/$database/_design/main_design" -u $username:$password \
-d @$current_dir/main_design.json
# Create data
curl -H "Content-Type: application/json" -d @$current_dir/books.json \
-X POST $url/$database/_bulk_docs -u $username:$password
echo
echo "Finished populating couchdb database '$database' on '$url'"
This is quite a long script as half of it concerned with parsing the command line to collect the username, password and url to our CouchDB instance. While we could hard-code these details, it’s convient to be able to re-use this script with other CouchDB instances hosted on the Internet.
We also need to make the seed_couchdb.sh
file executable:
$ chmod a+x scripts/seed_couchdb.sh
Seed the database
Now that we have our JSON data files and a bash script to use, we can populate our CouchDB database:
$ scripts/seed_couchdb.sh --username=rob --password=123456
(use the username and password that you set up earlier!)
The script will output some JSON for each curl command run and finish with this line:
Finished populating couchdb database 'bookshelf_db' on 'http://localhost:5984'
Test that the data exists
You can check the data has been populated into your database either by using Futon by opening http://localhost:5984/_utils in your browser. More easily, we can also use curl:
$ curl -s http://rob:123456@localhost:5984/bookshelf_db | jq
which will output something like:
{
"db_name": "bookshelf_db",
"doc_count": 28,
"doc_del_count": 0,
"update_seq": 28,
"purge_seq": 0,
"compact_running": false,
"disk_size": 8296,
"data_size": 7130,
"instance_start_time": "1470989118158273",
"disk_format_version": 6,
"committed_update_seq": 28
}
(The jq tool nicely formats the JSON to make it readable)
The key information is that the doc_count
is 28. This is our 27 books plus one design document.
Database access via curl
We can also retrieve the output of the design document’s view:
$ curl -s http://rob:123456@127.0.0.1:5984/bookshelf_db/_design/main_design/_view/all_books | jq
This will return all the books in the database.
Similarly we can add a new book by POSTing to the database:
$ curl -s -X POST -H "Content-type: application/json" http://rob:123456@127.0.0.1:5984/bookshelf_db \
-d '{"type": "book", "author": "Julie Czerneda", "title": "Beholder\u0027s Eye", "isbn": "9780886779863"}' | jq
which will output something like:
{
"ok": true,
"id": "feab44ebffb57f3e32c93c6e06065a62",
"rev": "1-0794e8aebf9381475611b6e5bf385dc4"
}
We can edit a book, by PUTing a complete representation to the record’s URL and including the current revision in the body as the _rev
key:
$ curl -s -X PUT -H "Content-type: application/json" http://rob:123456@127.0.0.1:5984/bookshelf_db/feab44ebffb57f3e32c93c6e06065a62 \
-d '{"type": "book", "author": "Julie E. Czerneda", "title": "Beholder\u0027s Eye", "isbn": "9780886779863", "_rev": "1-0794e8aebf9381475611b6e5bf385dc4"}' | jq
The output is the same as for creating a record, except that the revision number is different:
{
"ok": true,
"id": "feab44ebffb57f3e32c93c6e06065a62",
"rev": "2-f413d2fdaa416a92e66f94f3767b07ae"
}
To delete the a book, we use the DELETE HTTP verb:
$ curl -s -X DELETE -H "Content-type: application/json" http://rob:123456@127.0.0.1:5984/bookshelf_db \
-d '{"id": "feab44ebffb57f3e32c93c6e06065a62", "rev": "2-f413d2fdaa416a92e66f94f3767b07ae"}' | jq
We now have a working CouchDB instance with some data in it so we can move along to part 3 and retrieve it in our Swift application!
Aside: Update the docker-composer file
If you’re using docker compose
, then update the docker-compose.yml
file to start up a CouchDB container and also seed the database:
docker-compose.yml
db:
image: couchdb
ports:
- "5984:5984"
environment:
- COUCHDB_USER=rob
- COUCHDB_PASSWORD=123456
seed-db:
image: ibmcom/swift-ubuntu
volumes:
- .:/root/BookshelfAPI
command: BookshelfAPI/scripts/seed_couchdb.sh --username=rob --password=123456 --url=http://db:5984
links:
- db
app:
image: ibmcom/swift-ubuntu
ports:
- "8090:8090"
volumes:
- .:/root/BookshelfAPI
command: bash -c "make clean -C BookshelfAPI && make -C BookshelfAPI && BookshelfAPI/.build/debug/BookshelfAPI"
links:
- db:localhost
Tutorial navigation
- Part 1: Getting Started with Kitura
- Part 2: CouchDB
- Part 3: List books
- Part 4: Configuration
- Part 5: Hypermedia
- Part 6: Adding a book
GitHub repository: kitura_bookshelfapi