JSON Schema: Your API’s
Secret Weapon
API Craft Boston / 2016-03-10
Pete Gamache / pete@appcues.com / @gamache
JSON Schema
Describes the structure of JSON data using a JSON-based
language
Standards-track
Simple
Great at nested objects
Generally treated as documentation
Good library support, though
Example: Event
{

"name": "button_click",

"timestamp": 1457437187,

"attributes": {

"button_id": 271828,

"page": "/"

}

}
Example JSON Schema
{

"$schema": "http://json-schema.org/draft-04/schema#",

"title": "Example Schema",

"definitions": {

"event": {

"type": "object",

"required": ["name", "timestamp"],

"properties": {

"name": {"type": "string"},

"timestamp": {"type": "integer"},

"attributes": {"type": "object"}

}

},

// ...
Example: Event Collection
{

"events": [

{

"name": "button_click",

"timestamp": 1457437187,

"attributes": {

"button_id": 271828,

"page": "/"

}

},

// ...

]

}
Example JSON Schema
{

"$schema": "http://json-schema.org/draft-04/schema#",

"title": "Example Schema",

"definitions": { // ...

"event_collection": {

"required": ["events"],

"properties": {

"events": {

"type": "array",

"items": {"ref": "#/definitions/event"}

}

}

},

// ...
It would be a shame for a
lovely, machine-readable
doc like that to be wasted
on humans...
JSON Validation with Elixir
and ExJsonSchema
iex> schema = File.read!("schema.json") |> Poison.decode!
|> ExJsonSchema.Schema.resolve



iex> event_schema = schema.schema["definitions"]["event"]



iex> ExJsonSchema.Validator.validate(schema,
event_schema, %{})

[{"Required property name was not present.", []},

{"Required property timestamp was not present.", []}]



iex> ExJsonSchema.Validator.validate(schema,
event_schema, %{"name" => "hi", "timestamp" => 1})

[]
JSON Validation with Elixir
and ExJsonSchema, cont.
iex> event_collection_schema = schema.schema["definitions"]
["event_collection"]



iex> ExJsonSchema.Validator.validate(schema, event_collection_schema,

...> %{"events" => [

...> %{"name" => "event 1", "attributes" => %{"awesome" => true}},

...> %{"name" => "event 2", "timestamp" => "whenever"},

...> %{"name" => "event 3", "timestamp" => 1234567890}

...> ]})

[{"Required property timestamp was not present.", ["events", 0]},

{"Expected "whenever" to be a valid ISO 8601 date-time.",

["events", 1, "timestamp"]}]
Use Case 1: Input Validation
Writing data validators is a pain, especially for anything
complex
Not only do we have to validate input, we need to
generate coherent error messages
Lots of opportunity to reinvent the wheel, but let's not
API Input Validation with
Elixir and ExJsonSchema
defmodule MyApp.EventsController do

use MyApp.Web, :controller

plug :validate_params



defp validate_params(conn, _params) do

case JsonSchema.validate(conn.params, :event_collection) do

[] ->

conn |> assign(:event_collection, conn.params)

errors ->

json_errors = errors |> JsonSchema.errors_to_json

conn |> put_status(422) |> json(%{errors: json_errors}) |> halt

end

end



def save_events(conn, params) do

event_collection = conn.assigns[:event_collection]

# ... do something here

conn |> put_status(202) |> json(%{ok: true})

end

end
Use Case 2: Output Validation
Pointing to the JSON Schema in API docs is great for
humans
Performing JSON Schema validation in API tests
ensures your docs aren't lying*. This is also great for
humans

* at least not about output data structure format
API Output Validation with
Elixir and ExJsonSchema
defmodule MyApp.EventsControllerTest do

use Plug.Test



test "it returns well-formed event collection" do

resp = conn(:post, "url goes here", %{params: ...})

|> MyApp.Router.call(MyApp.Router.init([]))

resp_object = resp.resp_body |> Poison.decode!

assert([] ==

JsonSchema.validate(resp_object, :event_collection))

end



# ... more tests here

end
References
http://json-schema.org/
JSON Pointer -- https://tools.ietf.org/html/rfc6901
https://github.com/jonasschmidt/ex_json_schema
https://engineering.appcues.com/2016/01/20/ex-json-
schema.html
http://www.slideshare.net/petegamache/
jsonschema20160310
Questions?
Love APIs? Appcues is hiring!
APIs in Elixir and ES6/AWS Lambda/API Gateway
Frontend in ES6/Redux/React
http://tinyurl.com/appcues-full-stack

JSON Schema: Your API's Secret Weapon

  • 1.
    JSON Schema: YourAPI’s Secret Weapon API Craft Boston / 2016-03-10 Pete Gamache / pete@appcues.com / @gamache
  • 2.
    JSON Schema Describes thestructure of JSON data using a JSON-based language Standards-track Simple Great at nested objects Generally treated as documentation Good library support, though
  • 3.
    Example: Event {
 "name": "button_click",
 "timestamp":1457437187,
 "attributes": {
 "button_id": 271828,
 "page": "/"
 }
 }
  • 4.
    Example JSON Schema {
 "$schema":"http://json-schema.org/draft-04/schema#",
 "title": "Example Schema",
 "definitions": {
 "event": {
 "type": "object",
 "required": ["name", "timestamp"],
 "properties": {
 "name": {"type": "string"},
 "timestamp": {"type": "integer"},
 "attributes": {"type": "object"}
 }
 },
 // ...
  • 5.
    Example: Event Collection {
 "events":[
 {
 "name": "button_click",
 "timestamp": 1457437187,
 "attributes": {
 "button_id": 271828,
 "page": "/"
 }
 },
 // ...
 ]
 }
  • 6.
    Example JSON Schema {
 "$schema":"http://json-schema.org/draft-04/schema#",
 "title": "Example Schema",
 "definitions": { // ...
 "event_collection": {
 "required": ["events"],
 "properties": {
 "events": {
 "type": "array",
 "items": {"ref": "#/definitions/event"}
 }
 }
 },
 // ...
  • 7.
    It would bea shame for a lovely, machine-readable doc like that to be wasted on humans...
  • 8.
    JSON Validation withElixir and ExJsonSchema iex> schema = File.read!("schema.json") |> Poison.decode! |> ExJsonSchema.Schema.resolve
 
 iex> event_schema = schema.schema["definitions"]["event"]
 
 iex> ExJsonSchema.Validator.validate(schema, event_schema, %{})
 [{"Required property name was not present.", []},
 {"Required property timestamp was not present.", []}]
 
 iex> ExJsonSchema.Validator.validate(schema, event_schema, %{"name" => "hi", "timestamp" => 1})
 []
  • 9.
    JSON Validation withElixir and ExJsonSchema, cont. iex> event_collection_schema = schema.schema["definitions"] ["event_collection"]
 
 iex> ExJsonSchema.Validator.validate(schema, event_collection_schema,
 ...> %{"events" => [
 ...> %{"name" => "event 1", "attributes" => %{"awesome" => true}},
 ...> %{"name" => "event 2", "timestamp" => "whenever"},
 ...> %{"name" => "event 3", "timestamp" => 1234567890}
 ...> ]})
 [{"Required property timestamp was not present.", ["events", 0]},
 {"Expected "whenever" to be a valid ISO 8601 date-time.",
 ["events", 1, "timestamp"]}]
  • 10.
    Use Case 1:Input Validation Writing data validators is a pain, especially for anything complex Not only do we have to validate input, we need to generate coherent error messages Lots of opportunity to reinvent the wheel, but let's not
  • 11.
    API Input Validationwith Elixir and ExJsonSchema defmodule MyApp.EventsController do
 use MyApp.Web, :controller
 plug :validate_params
 
 defp validate_params(conn, _params) do
 case JsonSchema.validate(conn.params, :event_collection) do
 [] ->
 conn |> assign(:event_collection, conn.params)
 errors ->
 json_errors = errors |> JsonSchema.errors_to_json
 conn |> put_status(422) |> json(%{errors: json_errors}) |> halt
 end
 end
 
 def save_events(conn, params) do
 event_collection = conn.assigns[:event_collection]
 # ... do something here
 conn |> put_status(202) |> json(%{ok: true})
 end
 end
  • 12.
    Use Case 2:Output Validation Pointing to the JSON Schema in API docs is great for humans Performing JSON Schema validation in API tests ensures your docs aren't lying*. This is also great for humans
 * at least not about output data structure format
  • 13.
    API Output Validationwith Elixir and ExJsonSchema defmodule MyApp.EventsControllerTest do
 use Plug.Test
 
 test "it returns well-formed event collection" do
 resp = conn(:post, "url goes here", %{params: ...})
 |> MyApp.Router.call(MyApp.Router.init([]))
 resp_object = resp.resp_body |> Poison.decode!
 assert([] ==
 JsonSchema.validate(resp_object, :event_collection))
 end
 
 # ... more tests here
 end
  • 14.
    References http://json-schema.org/ JSON Pointer --https://tools.ietf.org/html/rfc6901 https://github.com/jonasschmidt/ex_json_schema https://engineering.appcues.com/2016/01/20/ex-json- schema.html http://www.slideshare.net/petegamache/ jsonschema20160310
  • 15.
  • 16.
    Love APIs? Appcuesis hiring! APIs in Elixir and ES6/AWS Lambda/API Gateway Frontend in ES6/Redux/React http://tinyurl.com/appcues-full-stack