{"id":2193,"date":"2015-12-23T19:16:31","date_gmt":"2015-12-24T01:16:31","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=2193"},"modified":"2016-02-20T09:20:51","modified_gmt":"2016-02-20T15:20:51","slug":"building-rest-apis-with-zewo","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/building-rest-apis-with-zewo\/","title":{"rendered":"Building REST APIs with Zewo"},"content":{"rendered":"<p><b>2\/20\/2016 Update<\/b>:  Zewo has undergone a significant refactoring since this tutorial was originally written.  The information below is no longer accurate and I will be working to provide a new tutorial as soon as possible.<\/p>\n<h2>Zewo<\/h2>\n<p><a href=\"https:\/\/github.com\/Zewo\/\">Zewo<\/a> is new collection of modules designed to build web applications in Swift.  Zewo provides:<\/p>\n<ul>\n<li>an HTTP server (<a href=\"https:\/\/github.com\/zewo\/epoch\">Epoch<\/a>)\n<li>an HTTP router\n<li>HTTP request and response entity classes\n<li>an interface to a <a href=\"https:\/\/mustache.github.io\/\">Mustache<\/a> template responder (Sideburns)\n<li>PostgreSQL and MySQL database adapters\n<\/ul>\n<p>and much more.  Although relatively new to the scene, Zewo already has enough functionality to build a complete REST-based web service application.  In the past I would have first considered Ruby and <a href=\"http:\/\/www.sinatrarb.com\/\">Sinatra<\/a>, but now with the arrival of <a href=\"http:\/\/dev.iachieved.it\/iachievedit\/swift-on-linux\/\">Swift on Linux<\/a>, Zewo provides another great option.<\/p>\n<h2>A Quick API Design<\/h2>\n<p>We&#8217;re going to build a REST API similar to that of AT&amp;T&#8217;s <a href=\"https:\/\/m2x.att.com\/\">M2X<\/a> platform.  M2X is self-described as providing <i>time-series data storage, device management, message brokering, event triggering, alarming, and data visualization for your industrial Internet of Things (IOT) products and services<\/i>.  That&#8217;s a mouthful.  Our API will be less ambitious and implement the time-series data storage piece, but that is enough to showcase some of the capabilities of Zewo.<\/p>\n<p>In the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Internet_of_Things\">IOT<\/a> world we can think about <i>devices<\/i> and <i>streams<\/i> of data that are associated with the device.  For example, a device could be a single-board computer like a BeagleBone or Raspberry Pi with several sensors attached to it.  The sensor, in turn, would be providing a <i>stream<\/i> of <i>datapoints<\/i>, say temperature readings.  These concepts can be modeled nicely in a relational database fronted by a REST API.  And, if relational databases aren&#8217;t your thing, folks are working on NoSQL interfaces such as <a href=\"https:\/\/github.com\/danappelxx\/swiftmongodb\">MongoDB<\/a>.<\/p>\n<h3>Devices, Streams, and Datapoints<\/h3>\n<p>Our model will consist of <code>Device<\/code>, <code>Stream<\/code>, and <code>Datapoint<\/code>.<\/p>\n<p>A <code>Device<\/code> has the following attributes, or properties:<\/p>\n<ul>\n<li>name\n<li>serial\n<li>location\n<li>streams\n<\/ul>\n<p>A <code>Stream<\/code> has the following properties:<\/p>\n<ul>\n<li>name\n<li>datapoints\n<\/ul>\n<p>And finally, a <code>Datapoint<\/code> (sometimes referred to as a <i>reading<\/i>) will be:<\/p>\n<ul>\n<li>timestamp\n<li>value\n<\/ul>\n<p>Now, believe me, this is the not the end-all be-all data model for an IoT application, but it provides enough structure to make our REST API interesting.<\/p>\n<p>The operations we will support are:<\/p>\n<ul>\n<li>creating a new device\n<li>posting a value to a stream\n<li>retrieving the values of a stream\n<\/ul>\n<p>Of course, this is limited functionality, so it is left up to the reader to add things like:<\/p>\n<ul>\n<li>updating the location of the device\n<li>adding a new stream to a device\n<\/ul>\n<p>So, let&#8217;s dive in!<\/p>\n<h2>First Things First<\/h2>\n<p>The example code provided at the end of the tutorial has only been tested on Linux, and everything done here is on an Ubuntu 14.04 system.  To get up and running you will need to:<\/p>\n<ul>\n<li><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/ubuntu-packages-for-open-source-swift\/\">install Swift<\/a>\n<li><a href=\"https:\/\/help.ubuntu.com\/community\/PostgreSQL\">install Postgres<\/a>\n<li>create a Postgres database named <code>iot_staging<\/code>\n<li>create a Postgres user named <code>iotuser<\/code> with the password <code>iotuser<\/code>\n<\/ul>\n<p>There are detailed instructions on these steps further below.<\/p>\n<p>Once the prerequisites are installed we&#8217;re going to use the Swift Package Manager to bootstrap in our Zewo dependencies.  In a directory named <code>iotapp<\/code> create your <code>Package.swift<\/code> file:<\/p>\n<pre class=\"lang:swift\">\nimport PackageDescription\n\nlet package = Package(\n  name: \"iotapp\",\n  dependencies:[\n    .Package(url:\"https:\/\/github.com\/Zewo\/Epoch\", majorVersion:0, minor:1),\n    .Package(url:\"https:\/\/github.com\/Zewo\/Middleware\", majorVersion:0, minor:1),\n    .Package(url:\"https:\/\/github.com\/Zewo\/PostgreSQL\", majorVersion: 0, minor:1),\n  ]\n)\n<\/pre>\n<p>and then run <code>swift build<\/code>.  You will see the Swift Package Manager download and compile all of the required packages:<\/p>\n<pre class=\"crayon:false\">\nCloning Packages\/Epoch\nUsing version 0.1.0 of package Epoch\nCloning Packages\/HTTPParser\nUsing version 0.1.0 of package HTTPParser\nCloning Packages\/CHTTPParser\nUsing version 0.1.0 of package CHTTPParser\nCloning Packages\/HTTP\nUsing version 0.1.2 of package HTTP\nCloning Packages\/Core\nUsing version 0.1.1 of package Core\nCloning Packages\/CURIParser\nUsing version 0.1.0 of package CURIParser\nCloning Packages\/Venice\nUsing version 0.1.0 of package Venice\nCloning Packages\/CLibvenice\nUsing version 0.1.0 of package CLibvenice\nCloning Packages\/Middleware\nUsing version 0.1.0 of package Middleware\nCloning Packages\/Router\nUsing version 0.1.0 of package Router\nCloning Packages\/PostgreSQL\nUsing version 0.1.0 of package PostgreSQL\nCloning Packages\/SQL\nUsing version 0.1.0 of package SQL\nCloning Packages\/CLibpq\nUsing version 0.1.0 of package CLibpq\nCompiling Swift Module 'Core' (15 sources)\nLinking Library:  .build\/debug\/Core.a\nCompiling Swift Module 'HTTP' (10 sources)\nLinking Library:  .build\/debug\/HTTP.a\nCompiling Swift Module 'HTTPParser' (4 sources)\nLinking Library:  .build\/debug\/HTTPParser.a\nCompiling Swift Module 'Venice' (21 sources)\nLinking Library:  .build\/debug\/Venice.a\nCompiling Swift Module 'Epoch' (9 sources)\nLinking Library:  .build\/debug\/Epoch.a\nCompiling Swift Module 'Router' (1 sources)\nLinking Library:  .build\/debug\/Router.a\nCompiling Swift Module 'Middleware' (11 sources)\nLinking Library:  .build\/debug\/Middleware.a\nCompiling Swift Module 'SQL' (6 sources)\nLinking Library:  .build\/debug\/SQL.a\nCompiling Swift Module 'PostgreSQL' (5 sources)\nLinking Library:  .build\/debug\/PostgreSQL.a\n<\/pre>\n<p><b>Note:<\/b>  If the above bombed out it is likely due to missing dependencies.  You don&#8217;t need to perform the bootstrap steps just yet, but if you&#8217;re chomping at the bit run the following:<\/p>\n<pre class=\"crayon:false\">\nsudo apt-get install libpq-dev\necho \"deb [trusted=yes] http:\/\/apt.zewo.io\/deb .\/\" | sudo tee --append \/etc\/apt\/sources.list\nsudo apt-get update\nsudo apt-get install uri-parser http-parser libvenice \n<\/pre>\n<h2>Anatomy of a Zewo REST Application<\/h2>\n<p>A Zewo REST application looks similar to other HTTP application stacks such as <a href=\"http:\/\/rubyonrails.org\/\">Rails<\/a>, <a href=\"http:\/\/www.sinatrarb.com\/\">Sinatra<\/a>, etc.  At the top of the stack is the familiar HTTP Server.  Zewo provides us with <a href=\"https:\/\/github.com\/zewo\/epoch\">Epoch<\/a>, described as a <i>Venice based HTTP server for Swift 2.2 on Linux<\/i>.  A straightforward <code>main.swift<\/code> initializes our server:<\/p>\n<pre class=\"lang:swift\">\nimport Epoch\n\nServer(port:8080, responder:router).start()\n<\/pre>\n<p>Next down the stack is our routing functionality, which is an instance of Zewo <a href=\"https:\/\/github.com\/zewo\/router\">Router<\/a>.  The <code>router<\/code> is initialized in <code>Router.swift<\/code> which looks like this:<\/p>\n<pre>\nimport Router\nimport Middleware\n\nlet router = Router { route in\n  route.router(\"\/api\", APIv1 >>> log)\n}\n<\/pre>\n<p>The <code>>>><\/code> admittedly looks like a little black magic.  It is an overloaded operator defined in Zewo <a href=\"https:\/\/github.com\/Zewo\/Middleware\">Middleware<\/a>.  In this example it provides for logging our API actions.<\/p>\n<p>The <code>route.router(\"\/api\", APIv1 >>> log)<\/code> statement will anchor our web service at <code>\/api<\/code>.  Let&#8217;s look now at <code>APIv1.swift<\/code>:<\/p>\n<pre class=\"lang:swift\">\nimport HTTP\nimport Router\nimport Middleware\n\nlet APIv1 = Router(\"\/v1\") {\n  route in\n  route.get(\"\/version\") {\n    _ in\n    return Response(status: .OK,\n                    json:[\"version\":\"1.0.0\"])\n  }\n\n  \/\/ Create a new device\n  route.post(\"\/devices\", parseJSON >>> deviceController.create)\n\n  \/\/ Get a device\n  route.get(\"\/devices\/:serial\", deviceController.show)\n\n  \/\/ Post a datapoint to a stream\n  route.post(\"\/devices\/:serial\/streams\/:name\/value\",\n             parseJSON >>> datapointController.create)\n\n  \/\/ Get values posted to a stream\n  route.get(\"\/devices\/:serial\/streams\/:name\", datapointController.index)\n\n}\n<\/pre>\n<p><code>APIv1<\/code> is where the real action is happening.  Our URL anchor point is now extended to <code>\/api\/v1<\/code> and we&#8217;ve specified a number of specific routes from there:<\/p>\n<ul>\n<li><code>GET \/api\/v1\/version<\/code>\n<li><code>POST \/api\/v1\/devices<\/code>\n<li><code>GET \/api\/v1\/devices\/:serial<\/code>\n<li><code>POST \/api\/v1\/devices\/:serial\/streams\/:name\/value<\/code>\n<li><code>GET \/api\/v1\/devices\/:serial\/streams\/:name<\/code>\n<\/ul>\n<p>Again, while not the richest API, it serves as starting point.<\/p>\n<p>If you&#8217;ve ever worked with <a href=\"http:\/\/www.sinatrarb.com\/\">Sinatra<\/a> you will recognize the <i>named parameters<\/i> in the route patterns.  These parameters, such as <code>:serial<\/code> and <code>:name<\/code> will be provided to us in our controllers, which we&#8217;ll see next.<\/p>\n<h3>Controllers<\/h3>\n<p>Zewo does all the heavy lifting of receiving an HTTP request and then routing the request to the appropriate <i>controller<\/i>.  It is here though where we have to pick up the request and do something with it.<\/p>\n<p>What is a controller again exactly?  According to the Rails <a href=\"http:\/\/guides.rubyonrails.org\/action_controller_overview.html\"><code>ActionController<\/code><\/a> documentation a controller &#8220;is responsible for making sense of the request and producing the appropriate output&#8221;.  This is what our <code>DeviceController<\/code> does for us:<\/p>\n<pre class=\"style:swift\">\nimport Core\nimport HTTP\nimport Middleware\n\nlet deviceController = DeviceController()\n\nfinal class DeviceController {\n\n  let devices = try! DeviceRecord()\n  let streams = try! StreamRecord()\n\n  func create(request:Request) -> Response {\n    \n    guard let json = request.JSONBody else {\n      return Response(status:.BadRequest,\n                      json:[\n                        \"message\":\"No device definition found\"\n                      ])\n    }\n\n    guard let name = json[\"name\"]?.stringValue else {\n      return Response(status:.BadRequest,\n                      json:[\n                        \"message\":\"name property missing\"\n                      ])\n    }\n    \n    guard let serial = json[\"serial\"]?.stringValue else {\n      return Response(status:.BadRequest,\n                      json:[\n                        \"message\":\"serial property missing\"\n                      ])\n    }\n\n    var streamName:String\n    guard let streams = json[\"streams\"]?.arrayValue else {\n      return Response(status:.BadRequest,\n                      json:[\n                        \"message\":\"streams property missing\"\n                      ])\n    }\n    \n    \n    var location = ZERO_POINT\n    if let _location = json[\"location\"] {\n      location[\"latitude\"] = _location[\"latitude\"]!.doubleValue!\n      location[\"longitude\"] = _location[\"longitude\"]!.doubleValue! \n    }\n\n    if let device = devices.insert(name, serial:serial, location:location) {\n      for s in streams {\n        streamName = s[\"name\"]!.stringValue!\n        let stream = self.streams.insert(streamName, deviceId:device.id)\n        device.addStream(stream)\n      }\n      \n      return Response(status:.OK, json:device.toJSON())\n    } else {\n      return Response(status:.BadRequest, json:[\n                        \"message\":\"error creating device\"\n                      ])\n    }\n  }\n}\n<\/pre>\n<p>Recall that it was the <code>APIv1<\/code> router that calls <code>deviceController.create<\/code> when a <code>POST \/devices<\/code> is received.  The <code>create<\/code> function takes a <code>Request<\/code> object as its only argument, and then returns a <code>Response<\/code>.  In other words, the function is <i>making sense of the request and producing the appropriate output<\/i>.  Making sense of the request is broken down into distinct steps:<\/p>\n<ul>\n<li>obtaining the JSON body of the request and extracting required fields <code>name<\/code> and <code>serial<\/code>\n<li>obtaining the location field if it is available, otherwise the location defaults to (0.0, 0.0)\n<li>extracting the required <code>streams<\/code> field from the JSON\n<li>inserting a new <code>Device<\/code> into the database\n<li>inserting a new <code>Stream<\/code> for each stream specified\n<\/ul>\n<p>The response to this request can be one of two types:<\/p>\n<ul>\n<li><code>.BadRequest<\/code> (HTTP Status 400)\n<li><code>.OK<\/code> (HTTP Status 200)\n<\/ul>\n<p>In both cases we return a JSON body with additional information.<\/p>\n<p>An example of a valid JSON request body for <code>POST \/devices<\/code> looks like this:<\/p>\n<pre>\n{\"name\":\"BeagleBone\",\n \"serial\":\"NATT-8GXF-B7GF-RXNC\",\n \"location\":{\n  \"latitude\":-34.8836,\n  \"longitude\":-56.1819\n  },\n  \"streams\":[]\n}\n<\/pre>\n<p>If the request was successful we expect a response to look like:<\/p>\n<pre>\n{\n  \"serial\": \"NATT-8GXF-B7GF-RXNC\",\n  \"streams\": [],\n  \"name\": \"BeagleBone\",\n  \"location\": {\n    \"longitude\":\"-56.1819\",\n    \"latitude\":\"-34.8836\"\n  },\n  \"id\": \"14\"\n}\n<\/pre>\n<p>The server returned an additional field <code>id<\/code> in the JSON response.  Some APIs are designed such that the client use the <code>id<\/code> field for future references to the created object.  Our API, on the other hand, will use the serial number to identify a given device.<\/p>\n<h3>Records<\/h3>\n<p>Observe that the <i>controller<\/i> does not interact directly with the database.  That is the job of our <i>record<\/i> class, which is modeled after the concept of <a href=\"http:\/\/guides.rubyonrails.org\/active_record_basics.html\"><code>ActiveRecord<\/code><\/a> in Rails.  The <code>DeviceRecord<\/code> class knows about Postgres, what table is used to manage devices (in fact, it creates that table), and how to insert and find devices.  In Rails <code>ActiveRecord<\/code> is smart enough to read the table schema and create methods such as <code>find<\/code> on the fly.  Our <code>DeviceRecord<\/code> does not go that far; we aren&#8217;t building Rails here.<\/p>\n<p>This is the content of our <code>DeviceRecord.swift<\/code> file:<\/p>\n<pre class=\"lang:swift\">\nimport PostgreSQL\nimport CLibpq\nimport Foundation\n\nfinal class DeviceRecord {\n\n  let tableName = \"devices\"\n  let db = Connection(\"postgresql:\/\/iotuser:iotuser@localhost\/iot_staging\")\n\n  init() throws {\n    try db.open()\n    try db.execute(\"CREATE TABLE IF NOT EXISTS \\(tableName) (id SERIAL PRIMARY KEY, name VARCHAR(256), serial VARCHAR(256) UNIQUE, location POINT)\")\n  }\n\n  deinit {\n    db.close()\n  }\n\n  func insert(name:String, serial:String, location:Point) -> Device {\n\n    let stmt = \"INSERT into \\(tableName) (name,serial,location) VALUES('\\(name)','\\(serial)',POINT(\\(location[\"latitude\"]!),\\(location[\"longitude\"]!))) RETURNING id\"\n    \n    logmsg(stmt)\n    \n    let result = try! db.execute(stmt)\n    let id = result[0][\"id\"]!.string!\n    return Device(id:id, name:name, serial:serial, location:location)\n  }\n\n  func findBySerial(serial:String) -> Device? {\n\n    let stmt   = \"SELECT * from devices where serial = '\\(serial)'\"\n    \n    logmsg(stmt)\n    \n    let result = try! db.execute(stmt)\n    if result.count > 0 {\n      let id = result[0][\"id\"]!.string!\n      let name = result[0][\"name\"]!.string!\n      let serial = result[0][\"serial\"]!.string!\n      let location = result[0][\"location\"]!.string!\n      let locationAsPoint = pointFromString(location)\n      return Device(id:id, name:name, serial:serial, location:locationAsPoint)\n    }\n    return nil\n  }\n\n}\n<\/pre>\n<p>At a fundamental level there really isn&#8217;t a whole lot going on in this file, with of course the exception that we are issuing Postgres SQL commands to the underlying database adapter and reading the result.<\/p>\n<p>Before leaving the <code>DeviceRecord<\/code>, notice that <code>insert<\/code> returns <code>Device<\/code>.  This is <i>not<\/i> a good idea, and we should change the implementation to return <code>Device?<\/code> in the event there is an issue with the SQL <code>INSERT<\/code>.  The example code in Github will return <code>Device?<\/code> so we can catch these errors.<\/p>\n<p>Combining all the components together, top-to-bottom, we get a view of what the &#8220;stack&#8221; looks like:<\/p>\n<figure id=\"attachment_2258\" aria-describedby=\"caption-attachment-2258\" style=\"width: 181px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/12\/Basic_Zewo_App.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/12\/Basic_Zewo_App.png\" alt=\"Basic Web Service Stack\" width=\"181\" height=\"446\" class=\"size-full wp-image-2258\" \/><\/a><figcaption id=\"caption-attachment-2258\" class=\"wp-caption-text\">Basic Web Service Stack<\/figcaption><\/figure>\n<h2>A Working Example<\/h2>\n<p>Now that we have a basic idea of how Zewo is used to build a REST web service, let&#8217;s run a working example.<\/p>\n<p>First, install of the dependencies you&#8217;ll need, which includes <code>libpq-dev<\/code> for the Postgres headers, as well as some additional packages Zewo requires.<\/p>\n<pre class=\"crayon:false\">\nsudo apt-get install libpq-dev\necho \"deb [trusted=yes] http:\/\/apt.zewo.io\/deb .\/\" | sudo tee --append \/etc\/apt\/sources.list\nsudo apt-get update\nsudo apt-get install uri-parser http-parser libvenice \n<\/pre>\n<p>Now, get the code from Github and switch to the <code>basic<\/code> branch.<\/p>\n<pre class=\"crayon:false\">\ngit clone https:\/\/github.com\/iachievedit\/iotapp\ncd iotapp\ngit checkout basic\n<\/pre>\n<p>Build the application with <code>swift build<\/code> and run it:<\/p>\n<pre class=\"crayon:false\">\nswift build\n.build\/debug\/iotapp\n<\/pre>\n<p>If Postgres isn&#8217;t running or cannot otherwise be contacted, you might see this error:<\/p>\n<pre class=\"crayon:false\">\nfatal error: 'try!' expression unexpectedly raised an error: PostgreSQL.Connection.Error.ConnectFailed(\"could not connect to server: Connection refused\\n\\tIs the server running on host \\\"localhost\\\" (::1) and accepting\\n\\tTCP\/IP connections on port 5432?\\ncould not connect to server: Connection refused\\n\\tIs the server running on host \\\"localhost\\\" (127.0.0.1) and accepting\\n\\tTCP\/IP connections on port 5432?\\n\"): file swift\/stdlib\/public\/core\/ErrorType.swift, line 53\nIllegal instruction (core dumped)\n<\/pre>\n<p>In this case, install Postgres and create your user and database:<\/p>\n<pre class=\"crayon:false\">\n$ sudo apt-get install postgresql\n$ sudo su - postgres\n$ psql template1\npsql (9.4.5)\nType \"help\" for help.\n\ntemplate1=# CREATE USER iotuser WITH PASSWORD 'iotuser';\nCREATE ROLE\ntemplate1=# CREATE DATABASE iot_staging;\nCREATE DATABASE\ntemplate1=# GRANT ALL PRIVILEGES ON DATABASE \"iot_staging\" to iotuser;\nGRANT\ntemplate1=# \\q\n$ exit\n<\/pre>\n<p>Once the application is up and running, we can use cURL or Postman to create a new device.  Here we&#8217;ll use cURL:<\/p>\n<pre class=\"crayon:false\">\n$ curl -X POST -H \"Content-Type: application\/json\" -d '{\"name\":\"BeagleBone\", \"serial\":\"N9TT-8GXF-B7GF-RXNC\", \"location\":{\"latitude\":-34.8836, \"longitude\":-56.1819}, \"streams\":[{\"name\":\"cpu-occupancy\"}, {\"name\":\"free-memory\"}]}' 'http:\/\/localhost:8080\/api\/v1\/devices'\n<\/pre>\n<p>Our Zewo application processes the request, logging to STDOUT:<\/p>\n<pre class=\"crayon:false\">\n12\/23\/15, 8:11 PM \/home\/iotapp\/iotapp\/Sources\/DeviceRecord.swift:23 insert(_:serial:location:): INSERT into devices (name,serial,location) VALUES('BeagleBone','N9TT-8GXF-B7GF-RXNC',POINT(-34.8836,-56.1819)) RETURNING id\nPOST \/api\/v1\/devices HTTP\/1.1\nContent-Type: application\/json\nAccept: *\/*\nUser-Agent: curl\/7.43.0\nHost: localhost:8080\nContent-Length: 200\n\n{\"name\":\"BeagleBone\",\n \"serial\":\"N9TT-8GXF-B7GF-RXNC\",\n \"location\":{\n  \"latitude\":-34.8836,\n  \"longitude\":-56.1819\n  },\n  \"streams\":[\n      {\"name\":\"cpu-occupancy\"},\n      {\"name\":\"free-memory\"}\n  ]\n}\n-\nHTTP\/1.1 200 OK\ncontent-type: application\/json; charset=utf-8\nContent-Length: 128\n\n{\"id\":\"1\",\"name\":\"BeagleBone\",\"location\":{\"longitude\":-56.1819,\"latitude\":-34.8836},\"streams\":[],\"serial\":\"N9TT-8GXF-B7GF-RXNC\"}\n----------------------------------------\n<\/pre>\n<p>The response to our command is what we expect, a JSON string with our device!<\/p>\n<pre class=\"crayon:false\">\n{\"id\":\"1\",\"name\":\"BeagleBone\",\"location\":{\"longitude\":-56.1819,\"latitude\":-34.8836},\"streams\":[],\"serial\":\"N9TT-8GXF-B7GF-RXNC\"}\n<\/pre>\n<p>Try issuing the command again with the same serial number.  You should see <code>{\"message\":\"error creating device\"}<\/code> thanks to the logic we added to return a <code>.BadRequest<\/code> if we couldn&#8217;t insert the device.<\/p>\n<h2>Creating Streams and Posting Datapoints<\/h2>\n<p>We now need to add the functionality that creates <code>streams<\/code> and <code>datapoints<\/code>.  Unlike with <code>Device<\/code> we do not need a <code>StreamController<\/code>.  Streams are created directly through a <code>StreamRecord<\/code> instance in the <code>DeviceController<\/code>.<\/p>\n<p>Our <code>StreamRecord<\/code> code looks like this:<\/p>\n<pre class=\"lang:swift\">\nimport PostgreSQL\nimport CLibpq\nimport Foundation\n\nfinal class StreamRecord {\n\n  let tableName = \"streams\"\n  let db = Connection(\"postgresql:\/\/iotuser:iotuser@localhost\/iot_staging\")\n\n  init() throws {\n    try db.open()\n    try db.execute(\"CREATE TABLE IF NOT EXISTS \\(tableName) (id SERIAL PRIMARY KEY, name VARCHAR(256), device_id INTEGER NOT NULL)\")\n  }\n\n  deinit {\n    db.close()\n  }\n\n  func insert(name:String, deviceId:String) -> Stream {\n\n    let stmt = \"INSERT into \\(tableName) (name, device_id) VALUES('\\(name)', '\\(deviceId)') RETURNING id\"\n    \n    logmsg(stmt)\n\n    let result = try! db.execute(stmt)\n    let id     = result[0][\"id\"]!.string!\n    return Stream(id:id, name:name)\n           \n  }\n\n  func findByName(name:String, deviceId:String) -> Stream? {\n    let stmt = \"SELECT * from \\(tableName) where name = '\\(name)' and device_id = '\\(deviceId)'\"\n    \n    logmsg(stmt)\n\n    let result = try! db.execute(stmt)\n    \n    if result.count > 0 {\n        return Stream(id:result[0][\"id\"]!.string!,\n                      name:result[0][\"name\"]!.string!)\n    } else {\n      return nil\n    }\n  }\n\n}\n<\/pre>\n<p>Note that our <code>streams<\/code> schema definition contains <code>device_id<\/code>, which is a reference to the device that &#8220;owns&#8221; the stream.  Again, unlike with Rails, we are not specifying <code>has_a<\/code> or <code>belongs_to<\/code> relationships explicitly; we manage this through code.<\/p>\n<h3>Posting Datapoints<\/h3>\n<p>A <i>stream<\/i> is a collection of data points, or values.  For example, let&#8217;s say our BeagleBone device had a temperature sensor attached to it.  We would want to POST a temperature reading to the <code>temperature<\/code> stream, like this:<\/p>\n<pre class=\"crayon:false\">\ncurl -X POST -H \"Content-Type: application\/json\" -d '{\"value\":\"77\"}' 'http:\/\/localhost:8080\/api\/v1\/devices\/NFTT-8GXF-B7GF-RXNC\/streams\/temperature\/value'\n<\/pre>\n<p>Our API responds with:<\/p>\n<pre class=\"crayon:false\">\n{\n  \"value\": \"77\",\n  \"inserted_at\": \"2015-12-23 21:13:42.763442\",\n  \"id\": \"1\"\n}\n<\/pre>\n<p>Some time later the temperature increases to 78 degrees, so another value is posted.  Later on, it increases to 80 degrees, and so on.  Then, we can retrieve our datapoints via a GET request:<\/p>\n<pre class=\"crayon:false\">\ncurl -X GET -H \"Content-Type: application\/json\" 'http:\/\/localhost:8080\/api\/v1\/devices\/NFTT-8GXF-B7GF-RXNC\/streams\/temperature'\n<\/pre>\n<p>The following data is returned:<\/p>\n<pre class=\"crayon:false\">\n{\n  \"datapoints\": [\n    {\n      \"value\": \"77\",\n      \"inserted_at\": \"2015-12-23 21:13:42.763442\",\n      \"id\": \"1\"\n    },\n    {\n      \"value\": \"78\",\n      \"inserted_at\": \"2015-12-23 21:15:30.536468\",\n      \"id\": \"2\"\n    },\n    {\n      \"value\": \"80\",\n      \"inserted_at\": \"2015-12-23 21:15:54.651966\",\n      \"id\": \"3\"\n    }\n  ]\n}\n<\/pre>\n<p>Here is our implementation of <code>DatapointController<\/code>.  Note how the methods <code>DeviceRecord.findBySerial(serial:)<\/code> and <code>StreamRecord.findByName(name:,deviceId:)<\/code> are used to associate the datapoint with the correct device and stream.<\/p>\n<pre class=\"lang:swift\">\nimport Core\nimport HTTP\nimport Middleware\n\nlet datapointController = DatapointController()\n\nfinal class DatapointController {\n\n  let devices    = try! DeviceRecord()\n  let streams    = try! StreamRecord()\n  let datapoints = try! DatapointRecord()\n\n  func create(request:Request) -> Response {\n\n    logmsg(\"ENTRY\")\n\n    let deviceSerial = request.parameters[\"serial\"]!\n    let streamName   = request.parameters[\"name\"]!\n\n    guard let json = request.JSONBody, value = json[\"value\"]?.stringValue else {\n      return Response(status:.BadRequest)\n    }\n\n    guard let device = devices.findBySerial(deviceSerial) else {\n      return Response(status:.NotFound)\n    }\n\n    guard let stream = streams.findByName(streamName, deviceId:device.id) else {\n      return Response(status:.NotFound)\n    }\n\n    if let datapoint = datapoints.insert(value, streamId:stream.id) {\n      return Response(status:.OK, json:datapoint.toJSON())\n    }\n    \n    return Response(status:.BadRequest)\n\n  }\n  \n  func index(request:Request) -> Response {\n    logmsg(\"ENTRY\")\n\n    let deviceSerial = request.parameters[\"serial\"]!\n    let streamName   = request.parameters[\"name\"]!\n\n    guard let device = devices.findBySerial(deviceSerial) else {\n      return Response(status:.NotFound)\n    }\n\n    guard let stream = streams.findByName(streamName, deviceId:device.id) else {\n      return Response(status:.NotFound)\n    }\n\n    let values = datapoints.findByStream(stream.id, count:10)\n    let jsonValues = [\n      \"datapoints\":JSON.from(values)\n    ]\n\n    return Response(status:.OK, json:JSON.from(jsonValues))\n  \n  }\n\n}\n<\/pre>\n<p>Our <code>DatapointRecord<\/code> implementation looks like:<\/p>\n<pre class=\"lang:swift\">\nimport PostgreSQL\nimport CLibpq\nimport Foundation\n\nfinal class DatapointRecord {\n\n  let tableName = \"datapoints\"\n  let db = Connection(\"postgresql:\/\/iotuser:iotuser@localhost\/iot_staging\")\n\n  init() throws {\n    try db.open()\n    try db.execute(\"CREATE TABLE IF NOT EXISTS \\(tableName) (id SERIAL PRIMARY KEY, inserted_at TIMESTAMP WITHOUT TIME ZONE DEFAULT (now() AT TIME ZONE 'utc'), value TEXT, stream_id INTEGER NOT NULL)\")\n  }\n\n  deinit {\n    db.close()\n  }\n\n  func insert(value:String, streamId:String) -> Datapoint? {\n\n    let stmt = \"INSERT into \\(tableName) (value, stream_id) VALUES('\\(value)', '\\(streamId)') RETURNING id, inserted_at\"\n    logmsg(\"insert datapoint:  \\(stmt)\")\n\n    do {\n      let result = try db.execute(stmt)\n      let id     = result[0][\"id\"]!.string!\n      let inserted_at = result[0][\"inserted_at\"]!.string!\n      logmsg(\"datapoint inserted with id \\(id)\")\n      return Datapoint(id:id, value:value, inserted_at:inserted_at)\n    } catch {\n      return nil\n    }\n           \n  }\n\n  func findByStream(streamId:String, count:Int) -> [Datapoint] {\n    let stmt = \"SELECT * FROM \\(tableName) WHERE stream_id = \\(streamId) LIMIT \\(count)\"\n\n    logmsg(stmt)\n\n    var datapoints:[Datapoint] = []\n    do {\n      let results = try db.execute(stmt)\n      for result in results {\n        let id          = result[\"id\"]!.string!\n        let value       = result[\"value\"]!.string!\n        let inserted_at = result[\"inserted_at\"]!.string!\n        let datapoint   = Datapoint(id:id, value:value, inserted_at:inserted_at)\n        datapoints.append(datapoint)\n      }\n      return datapoints\n    } catch {\n      return []\n    }\n  }\n}\n<\/pre>\n<p><b>An exercise for the reader:<\/b>  The <code>DatapointRecord.findByStream(streamId:count:)<\/code> function is called with a hardcoded value of 10.  Extend the API to allow for the number of datapoints to be specified either by a JSON structure in the POST body, or through a URI query parameter.  Consider also adding <code>inserted_before<\/code> and <code>inserted_after<\/code> filters to control which values to return.<\/p>\n<h2>Complete Application<\/h2>\n<p>The completed IoT application is on <a href=\"https:\/\/github.com\/iachievedit\/iotapp\">Github<\/a> on the <code>master<\/code> branch.  With it you should be able to create devices (make sure and specify the streams up front or add an implementation of modifying a device to add a new stream!), and post datapoints to the device streams.<\/p>\n<p>There are some additional files and helper routines in the repository:<\/p>\n<ul>\n<li>the <code>+JSON<\/code> files provide <code>toJSON<\/code> extension functions to our objects\n<li><code>Utils.swift<\/code> adds a <code>logmsg<\/code> routine\n<\/ul>\n<p>As I mentioned, there are a <i>lot<\/i> of capabilities that can be added to this API.  Streams can be given datatypes and units, operations which find <code>min<\/code> and <code>max<\/code> datapoints in a stream could be added, and so on.  For inspiration check out the <a href=\"https:\/\/m2x.att.com\/developer\/documentation\/v2\/overview\">M2X API<\/a>.<\/p>\n<h2>What&#8217;s Next?<\/h2>\n<p>After spending time with the Zewo framework over the past week I&#8217;m convinced it will become one of the go to building blocks for developing web applications in Swift.  This article has focused on building the <i>server side<\/i> of the REST API, and I&#8217;m looking forward to realizing the dream of developing in one language when we look at developing a client side set of classes in Swift that can be used to interact with the service.  Stay tuned.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>2\/20\/2016 Update: Zewo has undergone a significant refactoring since this tutorial was originally written. The information below is no longer accurate and I will be working to provide a new tutorial as soon as possible. Zewo Zewo is new collection of modules designed to build web applications in Swift. Zewo provides: an HTTP server (Epoch) [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,5],"tags":[],"class_list":["post-2193","post","type-post","status-publish","format-standard","hentry","category-linux","category-swift"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/2193"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=2193"}],"version-history":[{"count":50,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/2193\/revisions"}],"predecessor-version":[{"id":2673,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/2193\/revisions\/2673"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=2193"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=2193"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=2193"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}