json schema: your api's secret weapon

16
JSON Schema: Your API’s Secret Weapon API Craft Boston / 2016-03-10 Pete Gamache / [email protected] / @gamache

Upload: pete-gamache

Post on 16-Feb-2017

739 views

Category:

Engineering


2 download

TRANSCRIPT

Page 1: JSON Schema: Your API's Secret Weapon

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

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

Example: Event

{ "name": "button_click", "timestamp": 1457437187, "attributes": { "button_id": 271828, "page": "/" }}

Page 4: JSON Schema: Your API's Secret Weapon

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

Example: Event Collection{ "events": [ { "name": "button_click", "timestamp": 1457437187, "attributes": { "button_id": 271828, "page": "/" } }, // ... ]}

Page 6: JSON Schema: Your API's Secret Weapon

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

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

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

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

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

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

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

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

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

Questions?

Page 16: JSON Schema: Your API's Secret Weapon

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