JSON Schema: Your API's Secret Weapon
-
Upload
pete-gamache -
Category
Engineering
-
view
725 -
download
2
Transcript of JSON Schema: Your API's Secret Weapon
![Page 1: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/1.jpg)
JSON Schema: Your API’s Secret Weapon
API Craft Boston / 2016-03-10 Pete Gamache / [email protected] / @gamache
![Page 2: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/2.jpg)
JSON SchemaDescribes 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
![Page 3: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/3.jpg)
Example: Event
{ "name": "button_click", "timestamp": 1457437187, "attributes": { "button_id": 271828, "page": "/" }}
![Page 4: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/4.jpg)
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"} } }, // ...
![Page 5: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/5.jpg)
Example: Event Collection{ "events": [ { "name": "button_click", "timestamp": 1457437187, "attributes": { "button_id": 271828, "page": "/" } }, // ... ]}
![Page 6: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/6.jpg)
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"} } } }, // ...
![Page 7: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/7.jpg)
It would be a shame for a lovely, machine-readable doc like that to be wasted on humans...
![Page 8: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/8.jpg)
JSON Validation with Elixir and ExJsonSchemaiex> schema = File.read!("schema.json") |> Poison.decode! |> ExJsonSchema.Schema.resolveiex> 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})[]
![Page 9: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/9.jpg)
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"]}]
![Page 10: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/10.jpg)
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
![Page 11: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/11.jpg)
API Input Validation with Elixir and ExJsonSchemadefmodule 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}) endend
![Page 12: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/12.jpg)
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
![Page 13: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/13.jpg)
API Output Validation with Elixir and ExJsonSchemadefmodule 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 hereend
![Page 14: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/14.jpg)
Referenceshttp://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
![Page 15: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/15.jpg)
Questions?
![Page 16: JSON Schema: Your API's Secret Weapon](https://reader030.fdocuments.us/reader030/viewer/2022021417/58a4bcd61a28ab2d688b65a5/html5/thumbnails/16.jpg)
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