Processing incoming data in a JSON API

Jonathan Guthrie
Server Side Swift and More
5 min readApr 7, 2017

One of the common things I get asked is how to process requests coming from mobile or web applications in the different formats.

The three main ways data can be submitted for a JSON API:

  1. Data passed in the URL (i.e. http://www.example.com/api/v1/stuff?var1=myName)
  2. As form params in a POST request
  3. As JSON submitted as the body of a POST, PATCH or PUT request.

So I’m going to start with a simple instance of the Perfect App Template:

$ git clone https://github.com/PerfectlySoft/PerfectAppTemplate.git jsontest
$ cd jsontest
$ swift package generate-xcodeproj

This creates an Xcode project… Open this, then open “Sources->Perfect-App-Template->configuration->Routes.swift”

On line 29, there’s a route for a GET request, URI “/“

If we now run this project as is then it will build the app and run it.

To run: select the executable target from the “Scheme” dropdown, then Run (CMD-R). Then, in your browser visit http://localhost:8181 — you will see “Hello, World!”

JSON Routes, and Testing with Postman

While we can easily test GET routes with a browser, it’s not quite so easy for other request types such as POST. This is where tools such as Postman step in — this allows us to simulate different types of requests with a whole range of options.

A JSON GET route

The two things we need to add are a route, and a handler for that route.

The route:

routes.append(["method":"get", "uri":"/api/v1/test", "handler":Handlers.JSONtestGET])

This points to a new route, Handlers.JSONtestGET, which we need to create:

static func JSONtestGET(data: [String:Any]) throws -> RequestHandler {
return {
request, response in
do {
try response.setBody(json: ["message":"Hello, World!, This is a GET Request"])
} catch {
print(error)
}
response.completed()
}
}

All this will do is echo back a simple “Hello, World!, This is a GET Request”

A JSON POST route

Next, we’re going to demonstrate how to have the same route directed to a different handler based on the HTTP verb. Similar to the GET request, we need to add a route and a handler.

The route:

routes.append(["method":"post", "uri":"/api/v1/test", "handler":Handlers.JSONtestPOST])

And now the JSONtestPOST function:

static func JSONtestPOST(data: [String:Any]) throws -> RequestHandler {
return {
request, response in
do {
try response.setBody(json: ["message":"Hello, World! This is a POST Request"])
} catch {
print(error)
}
response.completed()
}
}

Note that this is a basic route that has zero interaction — it is acting almost exactly like the GET request we looked at just before.

Now, lets set up a POST route to read a URL variable and echo it back.

Another JSON POST Route, with URL interaction

As before lets start with creating a route, then a handler:

routes.append(["method":"post", "uri":"/api/v1/testparam", "handler":Handlers.JSONtestPOSTvarURL])

And the handler:

static func JSONtestPOSTvarURL(data: [String:Any]) throws -> RequestHandler {
return {
request, response in
do {
if let name = request.param(name: "name") {
try response.setBody(json: ["message":"Hello, \(name)!"])
} else {
try response.setBody(json: ["message":"Hello, World! I can personalize this if you let me?"])
}
} catch {
print(error)
}
response.completed()
}
}

Really only one new thing here:
“if let name = request.param(name: “name”)” gives us the switch for if there is a param named “name”, and we use it as we echo back the response.

So in Postman we can construct a POST Route and submit it with a URL variable:

We can also see what happens when there is no parameter:

A POST route reading a form param

Breaking with the pattern, I’m not going to introduce a new route or handler…

Instead, in Postman we’ll construct POST params:

So you’ll see, the “request.param(name: “name”)” will pick up both GET (query) params from before, and POST params here.

Finally, a POST request to read JSON in the body of the request

The best way to submit information in an API context is actually to submit JSON in the body of the request.

This time we do need a new route and handler:

routes.append(["method":"post", "uri":"/api/v1/testbody", “handler":Handlers.JSONtestPOSTbody])

And the new handler, “Handlers.JSONtestPOSTbody”:

static func JSONtestPOSTbody(data: [String:Any]) throws -> RequestHandler {
return {
request, response in
do {
if let body = request.postBodyString {
let json = try body.jsonDecode() as? [String:Any]
let name = json?["name"] as? String ?? "Undefined"
try response.setBody(json: ["message":"Hello, \(name)!"])
} else {
try response.setBody(json: ["message":"Hello, World! I can personalize this if you let me?"])
}
} catch {
print(error)
}
response.completed()
}
}

So the variation from the last handler is this:

  • We introduce “request.postBodyString”, which gets us access to the JSON body posted.
  • Next, we decode that (let json = try body.jsonDecode() as? [String:Any])
  • And then we can access name/value pairs submitted in the JSON:
    let name = json?[“name”] as? String ?? “Undefined”
  • Finally, we can use those properties.

Looking at this in action with Postman, we can see that we can submit standardized JSON which can then be used and read in our app.

The full sourcecode for this article is available at:
https://github.com/PerfectExamples/JSON-API-POST

For examples visit GitHub.com/perfectexamples — there is an ever growing library of examples and demos there.

And of course if you’re looking for live help from our awesome community, join our Slack channel via www.perfect.ly

Thanks for being with us today, don’t forget to say Hi on Slack!

--

--

Jonathan Guthrie
Server Side Swift and More

Developer Evangelist, Musician, and Active Activist... Newmarket ON, Canada