The SRE Report 2024 - Great Findings for the teams
JSON Schema: Your API's Secret Weapon
1. JSON Schema: Your API’s
Secret Weapon
API Craft Boston / 2016-03-10
Pete Gamache / pete@appcues.com / @gamache
2. 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
7. It would be a shame for a
lovely, machine-readable
doc like that to be wasted
on humans...
8. 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})
[]
9. 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"]}]
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 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
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 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