SlideShare uma empresa Scribd logo
1 de 61
Baixar para ler offline
Alexander Zinchuk
SPECIFICATION-DRIVEN
DEVELOPMENT
ajaxy_ru
Alexander Zinchuk
toptal.com/resume/
alexander-zinchuk
anywaylabs.com
Executive EngineerSoftware Architect
ajaxy_ru
Alexander Zinchuk
SPECIFICATION-DRIVEN
DEVELOPMENT
ajaxy_ru
WHAT IS A
SPECIFICATION?
SPECIFICATION-DRIVEN DEVELOPMENT
ajaxy_ru
Industry standard
for REST API spec:
(FORMER SWAGGER)
SPECIFICATION-DRIVEN DEVELOPMENT
ajaxy_ru
MAINTAINING OPENAPI SPEC
{
"swagger": "2.0",
"info": {
"title": "Flightcall API 2.0",
"description": "This document describes HTTP REST JSON API",
"version": "2.0.0"
},
"host": "api.flightcall.flightvector.com",
"basePath": "/",
"schemes": [
"https"
],
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"securityDefinitions": {
"token": {
"name": "Authorization",
"type": "apiKey",
"in": "header"
}
},
"paths": {
"/organizations": {
"get": {
"summary": "List all organizations",
"description": "List all organizations",
"operationId": "GET--organizations",
"responses": {
"200": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Organization"
}
}
}
},
"tags": [
"Public endpoints"
]
}
},
"/organizations/{id}/ems_agencies": {
"get": {
"summary": "List all EMS agencies tied to a specific organization",
"description": "List all EMS agencies tied to a specific organization",
"operationId": "GET--organizations--id--ems_agencies",
"responses": {
"200": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/EmsAgency"
}
}
}
...
openapi.json
ajaxy_ru
MAINTAINING OPENAPI SPEC
{
"swagger": "2.0",
"info": {
"title": "Flightcall API 2.0",
"description": "This document describes HTTP REST JSON API",
"version": "2.0.0"
},
"host": "api.flightcall.flightvector.com",
"basePath": "/",
"schemes": [
"https"
],
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"securityDefinitions": {
"token": {
"name": "Authorization",
"type": "apiKey",
"in": "header"
}
},
"paths": {
"/organizations": {
"get": {
"summary": "List all organizations",
"description": "List all organizations",
"operationId": "GET--organizations",
"responses": {
"200": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Organization"
}
}
}
},
"tags": [
"Public endpoints"
]
}
},
"/organizations/{id}/ems_agencies": {
"get": {
"summary": "List all EMS agencies tied to a specific organization",
"description": "List all EMS agencies tied to a specific organization",
"operationId": "GET--organizations--id--ems_agencies",
"responses": {
"200": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/EmsAgency"
}
}
}
...
openapi.json
ajaxy_ru
MAINTAINING OPENAPI SPEC
{
"swagger": "2.0",
"info": {
"title": "Flightcall API 2.0",
"description": "This document describes HTTP REST JSON API",
"version": "2.0.0"
},
"host": "api.flightcall.flightvector.com",
"basePath": "/",
"schemes": [
"https"
],
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"securityDefinitions": {
"token": {
"name": "Authorization",
"type": "apiKey",
"in": "header"
}
},
"paths": {
"/organizations": {
"get": {
"summary": "List all organizations",
"description": "List all organizations",
"operationId": "GET--organizations",
"responses": {
"200": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Organization"
}
}
}
},
"tags": [
"Public endpoints"
]
}
},
"/organizations/{id}/ems_agencies": {
"get": {
"summary": "List all EMS agencies tied to a specific organization",
"description": "List all EMS agencies tied to a specific organization",
"operationId": "GET--organizations--id--ems_agencies",
"responses": {
"200": {
"description": "",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/EmsAgency"
}
}
}
...
openapi.json
VERBOSE AND BORING
ajaxy_ru
Attempts to optimize:
· Multiple files
· JSDoc
· Online editors and services
MAINTAINING OPENAPI SPEC
ajaxy_ru
Augmenting this presentation with
examples
MAINTAINING OPENAPI SPEC
TINYSPEC
ajaxy_ru
MAINTAINING OPENAPI SPEC
Imagine, we need to
Get users…
ajaxy_ru
User {name, age?: i, isAdmin: b}
user.models.tinyspec
MAINTAINING OPENAPI SPEC
ajaxy_ru
Imagine, we need to
Get users…
User {name, age?: i, isAdmin: b}
user.models.tinyspec
GET /users
=> {users: User[]}
users.endpoints.tinyspec
MAINTAINING OPENAPI SPEC
ajaxy_ru
Imagine, we need to
Get users…
npmjs.com/package/tinyspec
MAINTAINING OPENAPI SPEC
ajaxy_ru
User {name, age?: i, isAdmin: b}
GET /users
=> {users: User[]}
user.models.tinyspec
users.endpoints.tinyspec
{
"swagger": "2.0",
"info": {
"title": "API Example",
"description": "API Example",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"summary": "GET /users",
"description": "GET /users",
"operationId": "GET--users",
"responses": {
"200": {
"description": "",
"schema": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
},
"required": [
"users"
]
}
}
}
}
}
},
"tags": [],
"definitions": {
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"isAdmin": {
"type": "boolean"
}
},
"required": [
"name",
"isAdmin"
]
}
}
}
openapi.json
$ tinyspec -–json
MAINTAINING OPENAPI SPEC
ajaxy_ru
Imagine, we need to
Get users…
MAINTAINING OPENAPI SPEC
.org
ajaxy_ru
YOU CAN RE-USE
SPEC IN CODE!
SPECIFICATION-DRIVEN DEVELOPMENT
ajaxy_ru
1
ENDPOINT UNIT TESTS
ajaxy_ru
npmjs.com/package/supertest rubygems.org/gems/airborne
npmjs.com/package/chai-http
1 · ENDPOINT UNIT TESTS
ajaxy_ru
1 · ENDPOINT UNIT TESTS
describe('/users', () => {
it('List all users', async () => {
const { status, body: { users } } = request.get('/users');
expect(users[0].name).to.be('string');
expect(users[0].isAdmin).to.be('boolean');
expect(users[0].age).to.be.oneOf(['boolean', null]);
});
});
describe 'GET /users' do
it 'List all users' do
get '/users'
expect_json_types('users.*', {
name: :string,
isAdmin: :boolean,
age: :integer_or_null,
})
end
end
npmjs.com/package/supertest rubygems.org/gems/airborne
User {name, age?: i, isAdmin: b}
GET /users
=> {users: User[]}
user.models.tinyspec
users.endpoints.tinyspec
{
"swagger": "2.0",
"info": {
"title": "API Example",
"description": "API Example",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"summary": "GET /users",
"description": "GET /users",
"operationId": "GET--users",
"responses": {
"200": {
"description": "",
"schema": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"$ref": "#/definitions/User"
}
}
},
"required": [
"users"
]
}
}
}
}
}
},
"tags": [],
"definitions": {
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"isAdmin": {
"type": "boolean"
}
},
"required": [
"name",
"isAdmin"
]
}
}
}
openapi.json
$ tinyspec -–json
1 · ENDPOINT UNIT TESTS
ajaxy_ru
Imagine, we need to
Get users…
1 · ENDPOINT UNIT TESTS
json-schema.org
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"isAdmin": {
"type": "boolean"
}
},
"required": [
"name",
"isAdmin"
]
}
JSON SCHEMA
ajaxy_ru
1 · ENDPOINT UNIT TESTS
Any key-value object may be validated against JSON Schema
json-schema.org
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
},
"isAdmin": {
"type": "boolean"
}
},
"required": [
"name",
"isAdmin"
]
}
JSON SCHEMA
ajaxy_ru
npmjs.com/package/jest-ajv rubygems.org/gems/json_matchers
npmjs.com/package/chai-ajv-json-schema
1 · ENDPOINT UNIT TESTS
Any key-value object may be validated against JSON Schema
ajaxy_ru
1 · ENDPOINT UNIT TESTS
import deref from 'json-schema-deref-sync';
const schemas = deref(require('./openapi.json')).definitions;
describe('/users', () => {
it('List all users', async () => {
const { status, body: { users } } = request.get('/users');
expect(users[0]).toMatchSchema(schemas.User);
});
});
require ‘json_matchers/rspec'
JsonMatchers.schema_root = 'spec/schemas'
describe 'GET /users' do
it 'List all users' do
get '/users'
expect(result[:users][0]).to match_json_schema('User')
end
end
npmjs.com/package/jest-ajv rubygems.org/gems/json_matchers
2
USER-DATA VALIDATION
ajaxy_ru
2 · USER-DATA VALIDATION
User {name, age?: i, isAdmin: b}
user.models.tinyspec
Imagine, we need to
Update a user…
ajaxy_ru
2 · USER-DATA VALIDATION
User {name, age?: i, isAdmin: b}
UserUpdate !{name?, age?: i}
user.models.tinyspec
Imagine, we need to
Update a user…
ajaxy_ru
2 · USER-DATA VALIDATION
User {name, age?: i, isAdmin: b}
UserUpdate !{name?, age?: i}
user.models.tinyspec
Imagine, we need to
Update a user…
ajaxy_ru
2 · USER-DATA VALIDATION
User {name, age?: i, isAdmin: b}
UserUpdate !{name?, age?: i}
user.models.tinyspec
Imagine, we need to
Update a user…
ajaxy_ru
PATCH /users/:id {user: UserUpdate}
=> {success: b}
users.endpoints.tinyspec
2 · USER-DATA VALIDATION
UserUpdate !{name?, age?: i}
user.models.tinyspec
Imagine, we need to
Update a user…
ajaxy_ru
UserUpdate !{name?, age?: i}
PATCH /users/:id {user: UserUpdate}
=> {success: b}
user.models.tinyspec
users.endpoints.tinyspec
2 · USER-DATA VALIDATION
...
"paths": {
"/users/{id}": {
"patch": {
"summary": "PATCH /users/:id",
"description": "PATCH /users/:id",
"operationId": "PATCH--users--id",
"responses": {
"200": {
"description": "",
"schema": {
"type": "object",
"properties": {
"success": {
"type": "boolean"
}
},
"required": [
"success"
]
}
}
},
"parameters": [
{
"name": "id",
"type": "string",
"in": "path",
"required": true
},
{
"name": "body",
"required": true,
"schema": {
"type": "object",
"properties": {
"user": {
"$ref": "#/definitions/UserUpdate"
}
},
"required": [
"user"
]
},
"in": "body"
}
]
}
}
},
"tags": [],
"definitions": {
"UserUpdate": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"additionalProperties": false
}
}
openapi.json
Imagine, we need to
Update a user…
$ tinyspec -–json
ajaxy_ru
npmjs.com/package/ajv rubygems.org/gems/json-schema
2 · USER-DATA VALIDATION
ajaxy_ru
router.patch('/:id', async (ctx) => {
const updateData = ctx.body.user;
// Validation using JSON schema from API specification.
await validate(schemas.UserUpdate, updateData);
const user = await User.findById(ctx.params.id);
await user.update(updateData);
ctx.body = { success: true };
});
2 · USER-DATA VALIDATION
ajaxy_ru
router.patch('/:id', async (ctx) => {
const updateData = ctx.body.user;
// Validation using JSON schema from API specification.
await validate(schemas.UserUpdate, updateData);
const user = await User.findById(ctx.params.id);
await user.update(updateData);
ctx.body = { success: true };
});
2 · USER-DATA VALIDATION
FieldsValidationError {
error: b,
message,
fields: {name, message}[]
}
error.models.tinyspec
ajaxy_ru
router.patch('/:id', async (ctx) => {
const updateData = ctx.body.user;
// Validation using JSON schema from API specification.
await validate(schemas.UserUpdate, updateData);
const user = await User.findById(ctx.params.id);
await user.update(updateData);
ctx.body = { success: true };
});
2 · USER-DATA VALIDATION
FieldsValidationError {
error: b,
message,
fields: {name, message}[]
}
error.models.tinyspec
PATCH /users/:id {user: UserUpdate}
=> 200 {success: b}
=> 422 FieldsValidationError
users.endpoints.tinyspec
ajaxy_ru
3
MODEL SERIALIZATION
ajaxy_ru
3 · MODEL SERIALIZATION
{…}
DB TABLE JSON VIEWORM MODEL
ajaxy_ru
{…}
ORM MODEL JSON VIEW
serialization
DB TABLE
3 · MODEL SERIALIZATION
ajaxy_ru
{…}
{…}
{…}
DB TABLE
ASSOCIATED
MODELS ALTERNATE
REQUESTS
3 · MODEL SERIALIZATION
ajaxy_ru
Our Spec
has all needed
information!
3 · MODEL SERIALIZATION
{…}
{…}
{…}
ALTERNATE
REQUESTSajaxy_ru
Imagine, we need to
Get all users
with posts
and comments…
3 · MODEL SERIALIZATION
ajaxy_ru
Comment {authorId: i, message}
Post {topic, message, comments?: Comment[]}
User {name, isAdmin: b, age?: i}
UserWithPosts < User {posts: Post[]}
models.tinyspec
3 · MODEL SERIALIZATION
ajaxy_ru
Comment {authorId: i, message}
Post {topic, message, comments?: Comment[]}
User {name, isAdmin: b, age?: i}
UserWithPosts < User {posts: Post[]}
GET /blog/users
=> {users: UserWithPosts[]}
models.tinyspec
blogUsers.endpoints.tinyspec
3 · MODEL SERIALIZATION
ajaxy_ru
Comment {authorId: i, message}
Post {topic, message, comments?: Comment[]}
User {name, isAdmin: b, age?: i}
UserWithPosts < User {posts: Post[]}
GET /blog/users
=> {users: UserWithPosts[]}
models.tinyspec
blogUsers.endpoints.tinyspec
3 · MODEL SERIALIZATION
…
»paths»: {
"/blog/users": {
"get": {
"summary": "GET /blog/users",
"description": "GET /blog/users",
"operationId": "GET--blog--users",
"responses": {
"200": {
"description": "",
"schema": {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"$ref": "#/definitions/UserWithPosts"
}
}
},
"required": [
"users"
]
}
}
}
}
}
},
"tags": [],
"definitions": {
"Comment": {
"type": "object",
"properties": {
"authorId": {
"type": "integer"
},
"message": {
"type": "string"
}
},
"required": [
"authorId",
"message"
]
},
"Post": {
"type": "object",
"properties": {
"topic": {
"type": "string"
},
"message": {
"type": "string"
},
"comments": {
"type": "array",
"items": {
"$ref": "#/definitions/Comment"
}
}
},
"required": [
"topic",
"message"
]
},
"User": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"isAdmin": {
"type": "boolean"
},
"age": {
"type": "integer"
}
},
"required": [
"name",
"isAdmin"
]
},
"UserWithPosts": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"isAdmin": {
"type": "boolean"
},
"age": {
"type": "integer"
},
"posts": {
"type": "array",
"items": {
"$ref": "#/definitions/Post"
}
}
},
"required": [
"name",
"isAdmin",
"posts"
]
}
}
openapi.json
$ tinyspec -–json
ajaxy_ru
3 · MODEL SERIALIZATION
npmjs.com/package/sequelize-serialize
ajaxy_ru
3 · MODEL SERIALIZATION
import serialize from 'sequelize-serialize';
router.get('/blog/users', async (ctx) => {
const users = await User.findAll({
include: [{
association: User.posts,
include: [
Post.comments
]
}]
});
ctx.body = serialize(users, schemas.UserWithPosts);
});
npmjs.com/package/sequelize-serialize
ajaxy_ru
-=MAGIC=-
4
STATIC TYPING
ajaxy_ru
4 · STATIC TYPING
JSON SCHEMA
ajaxy_ru
npmjs.com/package/sw2dts
npmjs.com/package/swagger-to-flowtype
4 · STATIC TYPING
ajaxy_ru
4 · STATIC TYPING
$ tinyspec -–json
$ sw2dts ./openapi.json -o Api.d.ts --namespace Api
ajaxy_ru
4 · STATIC TYPING
$ tinyspec -–json
$ sw2dts ./openapi.json -o Api.d.ts --namespace Api
declare namespace Api {
export interface Comment {
authorId: number;
message: string;
}
export interface Post {
topic: string;
message: string;
comments?: Comment[];
}
export interface User {
name: string;
isAdmin: boolean;
age?: number;
}
export interface UserUpdate {
name?: string;
age?: number;
}
export interface UserWithPosts {
name: string;
isAdmin: boolean;
age?: number;
posts: Post[];
}
}
Api.d.ts
router.patch('/users/:id', async (ctx) => {
// Specify type for request data object
const userData: Api.UserUpdate = ctx.request.body.user;
// Run spec validation
await validate(schemas.UserUpdate, userData);
// Query the database
const user = await User.findById(ctx.params.id);
await user.update(userData);
// Return serialized result
const serialized: Api.User = serialize(user, schemas.User);
ctx.body = { user: serialized };
});
4 · STATIC TYPING
ajaxy_ru
it('Update user', async () => {
// Static check for test input data.
const updateData: Api.UserUpdate = { name: MODIFIED };
const res = await request.patch('/users/1', { user: updateData });
// Type helper for request response:
const user: Api.User = res.body.user;
expect(user).to.be.validWithSchema(schemas.User);
expect(user).to.containSubset(updateData);
});
4 · STATIC TYPING
router.patch('/users/:id', async (ctx) => {
// Specify type for request data object
const userData: Api.UserUpdate = ctx.request.body.user;
// Run spec validation
await validate(schemas.UserUpdate, userData);
// Query the database
const user = await User.findById(ctx.params.id);
await user.update(userData);
// Return serialized result
const serialized: Api.User = serialize(user, schemas.User);
ctx.body = { user: serialized };
});
ajaxy_ru
5
TYPE CASTING
ajaxy_ru
5 · TYPE CASTING
param1=value&param2=777&param3=false
Query params or non-JSON body:
ajaxy_ru
5 · TYPE CASTING
param1=value&param2=777&param3=false
{
param1: 'value',
param2: '777',
param3: 'false'
}
Query params or non-JSON body:
ajaxy_ru
npmjs.com/package/cast-with-schema
5 · TYPE CASTING
ajaxy_ru
import castWithSchema from 'cast-with-schema';
router.get('/posts', async (ctx) => {
// Cast parameters to expected types
const query = castWithSchema(ctx.query, schemas.PostsQuery);
// Run spec validation
await validate(schemas.PostsQuery, query);
// Query the database
const posts = await Post.search(query);
// Return serialized result
ctx.body = { posts: serialize(posts, schemas.Post) };
});
npmjs.com/package/cast-with-schema
5 · TYPE CASTING
ajaxy_ru
THANK YOU!
ajaxy_ru
github.com/Ajaxy/tinyspec anywaylabs.com

Mais conteúdo relacionado

Mais procurados

Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda Katz
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
Skills Matter
 
Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12
Stephan Hochdörfer
 

Mais procurados (20)

Take My Logs. Please!
Take My Logs. Please!Take My Logs. Please!
Take My Logs. Please!
 
KISS Automation.py
KISS Automation.pyKISS Automation.py
KISS Automation.py
 
Angular Tutorial Freshers and Experienced
Angular Tutorial Freshers and ExperiencedAngular Tutorial Freshers and Experienced
Angular Tutorial Freshers and Experienced
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
Integrate CI/CD Pipelines with Jira Software Cloud
Integrate CI/CD Pipelines with Jira Software CloudIntegrate CI/CD Pipelines with Jira Software Cloud
Integrate CI/CD Pipelines with Jira Software Cloud
 
Metrics-Driven Engineering
Metrics-Driven EngineeringMetrics-Driven Engineering
Metrics-Driven Engineering
 
Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]
Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]
Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]
 
Maintainable JavaScript 2012
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
Your code are my tests
Your code are my testsYour code are my tests
Your code are my tests
 
How did i steal your database
How did i steal your databaseHow did i steal your database
How did i steal your database
 
The JavaFX Ecosystem
The JavaFX EcosystemThe JavaFX Ecosystem
The JavaFX Ecosystem
 
Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
 
Take Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorksTake Data Validation Seriously - Paul Milham, WildWorks
Take Data Validation Seriously - Paul Milham, WildWorks
 
Frontin like-a-backer
Frontin like-a-backerFrontin like-a-backer
Frontin like-a-backer
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4
 
Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4Python Code Camp for Professionals 4/4
Python Code Camp for Professionals 4/4
 

Semelhante a APIdays Helsinki 2019 - Specification-Driven Development of REST APIs with Alexander Zinchuk, Toptal

Stratalux Cloud Formation and Chef Integration Presentation
Stratalux Cloud Formation and Chef Integration PresentationStratalux Cloud Formation and Chef Integration Presentation
Stratalux Cloud Formation and Chef Integration Presentation
Jeremy Przygode
 
Behavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using CucumberBehavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using Cucumber
KMS Technology
 

Semelhante a APIdays Helsinki 2019 - Specification-Driven Development of REST APIs with Alexander Zinchuk, Toptal (20)

Stratalux Cloud Formation and Chef Integration Presentation
Stratalux Cloud Formation and Chef Integration PresentationStratalux Cloud Formation and Chef Integration Presentation
Stratalux Cloud Formation and Chef Integration Presentation
 
Ams adapters
Ams adaptersAms adapters
Ams adapters
 
API REST et client Javascript - Nuxeo Tour 2014 - Workshop
API REST et client Javascript - Nuxeo Tour 2014 - WorkshopAPI REST et client Javascript - Nuxeo Tour 2014 - Workshop
API REST et client Javascript - Nuxeo Tour 2014 - Workshop
 
Serverless archtiectures
Serverless archtiecturesServerless archtiectures
Serverless archtiectures
 
Development in the could: How do we do it(Cloud computing. Microservices. Faas)
Development in the could: How do we do it(Cloud computing. Microservices. Faas)Development in the could: How do we do it(Cloud computing. Microservices. Faas)
Development in the could: How do we do it(Cloud computing. Microservices. Faas)
 
SDKs, the good the bad the ugly - Japan
SDKs, the good the bad the ugly - JapanSDKs, the good the bad the ugly - Japan
SDKs, the good the bad the ugly - Japan
 
Agile Testing Days 2018 - API Fundamentals - postman collection
Agile Testing Days 2018 - API Fundamentals - postman collectionAgile Testing Days 2018 - API Fundamentals - postman collection
Agile Testing Days 2018 - API Fundamentals - postman collection
 
infrastructure as code
infrastructure as codeinfrastructure as code
infrastructure as code
 
DevOps on AWS: Deep Dive on Infrastructure as Code - Toronto
DevOps on AWS: Deep Dive on Infrastructure as Code - TorontoDevOps on AWS: Deep Dive on Infrastructure as Code - Toronto
DevOps on AWS: Deep Dive on Infrastructure as Code - Toronto
 
Os Pruett
Os PruettOs Pruett
Os Pruett
 
AWS CloudFormation Best Practices
AWS CloudFormation Best PracticesAWS CloudFormation Best Practices
AWS CloudFormation Best Practices
 
Behavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using CucumberBehavior Driven Development and Automation Testing Using Cucumber
Behavior Driven Development and Automation Testing Using Cucumber
 
AngularJS Mobile Warsaw 20-10-2014
AngularJS Mobile Warsaw 20-10-2014AngularJS Mobile Warsaw 20-10-2014
AngularJS Mobile Warsaw 20-10-2014
 
IBM Cloud University: Build, Deploy and Scale Node.js Microservices
IBM Cloud University: Build, Deploy and Scale Node.js MicroservicesIBM Cloud University: Build, Deploy and Scale Node.js Microservices
IBM Cloud University: Build, Deploy and Scale Node.js Microservices
 
Deployment and Management on AWS:
 A Deep Dive on Options and Tools
Deployment and Management on AWS:
 A Deep Dive on Options and ToolsDeployment and Management on AWS:
 A Deep Dive on Options and Tools
Deployment and Management on AWS:
 A Deep Dive on Options and Tools
 
The JSON REST API for WordPress
The JSON REST API for WordPressThe JSON REST API for WordPress
The JSON REST API for WordPress
 
Android and REST
Android and RESTAndroid and REST
Android and REST
 
Automating your Infrastructure Deployment with CloudFormation and OpsWorks –...
 Automating your Infrastructure Deployment with CloudFormation and OpsWorks –... Automating your Infrastructure Deployment with CloudFormation and OpsWorks –...
Automating your Infrastructure Deployment with CloudFormation and OpsWorks –...
 
Elasticsearch in 15 Minutes
Elasticsearch in 15 MinutesElasticsearch in 15 Minutes
Elasticsearch in 15 Minutes
 
20160905 - BrisJS - nightwatch testing
20160905 - BrisJS - nightwatch testing20160905 - BrisJS - nightwatch testing
20160905 - BrisJS - nightwatch testing
 

Mais de apidays

Mais de apidays (20)

Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Apidays New York 2024 - The secrets to Graph success, by Leah Hurwich Adler, ...
Apidays New York 2024 - The secrets to Graph success, by Leah Hurwich Adler, ...Apidays New York 2024 - The secrets to Graph success, by Leah Hurwich Adler, ...
Apidays New York 2024 - The secrets to Graph success, by Leah Hurwich Adler, ...
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Apidays New York 2024 - API Discovery - From Crawl to Run by Rob Dickinson, G...
Apidays New York 2024 - API Discovery - From Crawl to Run by Rob Dickinson, G...Apidays New York 2024 - API Discovery - From Crawl to Run by Rob Dickinson, G...
Apidays New York 2024 - API Discovery - From Crawl to Run by Rob Dickinson, G...
 
Apidays Singapore 2024 - Building with the Planet in Mind by Sandeep Joshi, M...
Apidays Singapore 2024 - Building with the Planet in Mind by Sandeep Joshi, M...Apidays Singapore 2024 - Building with the Planet in Mind by Sandeep Joshi, M...
Apidays Singapore 2024 - Building with the Planet in Mind by Sandeep Joshi, M...
 
Apidays Singapore 2024 - Connecting Cross Border Commerce with Payments by Gu...
Apidays Singapore 2024 - Connecting Cross Border Commerce with Payments by Gu...Apidays Singapore 2024 - Connecting Cross Border Commerce with Payments by Gu...
Apidays Singapore 2024 - Connecting Cross Border Commerce with Payments by Gu...
 
Apidays Singapore 2024 - Privacy Enhancing Technologies for AI by Mark Choo, ...
Apidays Singapore 2024 - Privacy Enhancing Technologies for AI by Mark Choo, ...Apidays Singapore 2024 - Privacy Enhancing Technologies for AI by Mark Choo, ...
Apidays Singapore 2024 - Privacy Enhancing Technologies for AI by Mark Choo, ...
 
Apidays Singapore 2024 - Blending AI and IoT for Smarter Health by Matthew Ch...
Apidays Singapore 2024 - Blending AI and IoT for Smarter Health by Matthew Ch...Apidays Singapore 2024 - Blending AI and IoT for Smarter Health by Matthew Ch...
Apidays Singapore 2024 - Blending AI and IoT for Smarter Health by Matthew Ch...
 
Apidays Singapore 2024 - OpenTelemetry for API Monitoring by Danielle Kayumbi...
Apidays Singapore 2024 - OpenTelemetry for API Monitoring by Danielle Kayumbi...Apidays Singapore 2024 - OpenTelemetry for API Monitoring by Danielle Kayumbi...
Apidays Singapore 2024 - OpenTelemetry for API Monitoring by Danielle Kayumbi...
 
Apidays Singapore 2024 - Connecting Product and Engineering Teams with Testin...
Apidays Singapore 2024 - Connecting Product and Engineering Teams with Testin...Apidays Singapore 2024 - Connecting Product and Engineering Teams with Testin...
Apidays Singapore 2024 - Connecting Product and Engineering Teams with Testin...
 
Apidays Singapore 2024 - The Growing Carbon Footprint of Digitalization and H...
Apidays Singapore 2024 - The Growing Carbon Footprint of Digitalization and H...Apidays Singapore 2024 - The Growing Carbon Footprint of Digitalization and H...
Apidays Singapore 2024 - The Growing Carbon Footprint of Digitalization and H...
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Apidays Singapore 2024 - API Monitoring x SRE by Ryan Ashneil and Eugene Wong...
Apidays Singapore 2024 - API Monitoring x SRE by Ryan Ashneil and Eugene Wong...Apidays Singapore 2024 - API Monitoring x SRE by Ryan Ashneil and Eugene Wong...
Apidays Singapore 2024 - API Monitoring x SRE by Ryan Ashneil and Eugene Wong...
 
Apidays Singapore 2024 - A nuanced approach on AI costs and benefits for the ...
Apidays Singapore 2024 - A nuanced approach on AI costs and benefits for the ...Apidays Singapore 2024 - A nuanced approach on AI costs and benefits for the ...
Apidays Singapore 2024 - A nuanced approach on AI costs and benefits for the ...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
Apidays Singapore 2024 - How APIs drive business at BNP Paribas by Quy-Doan D...
Apidays Singapore 2024 - How APIs drive business at BNP Paribas by Quy-Doan D...Apidays Singapore 2024 - How APIs drive business at BNP Paribas by Quy-Doan D...
Apidays Singapore 2024 - How APIs drive business at BNP Paribas by Quy-Doan D...
 

Último

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 

Último (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 

APIdays Helsinki 2019 - Specification-Driven Development of REST APIs with Alexander Zinchuk, Toptal

  • 5. Industry standard for REST API spec: (FORMER SWAGGER) SPECIFICATION-DRIVEN DEVELOPMENT ajaxy_ru
  • 6. MAINTAINING OPENAPI SPEC { "swagger": "2.0", "info": { "title": "Flightcall API 2.0", "description": "This document describes HTTP REST JSON API", "version": "2.0.0" }, "host": "api.flightcall.flightvector.com", "basePath": "/", "schemes": [ "https" ], "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json" ], "securityDefinitions": { "token": { "name": "Authorization", "type": "apiKey", "in": "header" } }, "paths": { "/organizations": { "get": { "summary": "List all organizations", "description": "List all organizations", "operationId": "GET--organizations", "responses": { "200": { "description": "", "schema": { "type": "array", "items": { "$ref": "#/definitions/Organization" } } } }, "tags": [ "Public endpoints" ] } }, "/organizations/{id}/ems_agencies": { "get": { "summary": "List all EMS agencies tied to a specific organization", "description": "List all EMS agencies tied to a specific organization", "operationId": "GET--organizations--id--ems_agencies", "responses": { "200": { "description": "", "schema": { "type": "array", "items": { "$ref": "#/definitions/EmsAgency" } } } ... openapi.json ajaxy_ru
  • 7. MAINTAINING OPENAPI SPEC { "swagger": "2.0", "info": { "title": "Flightcall API 2.0", "description": "This document describes HTTP REST JSON API", "version": "2.0.0" }, "host": "api.flightcall.flightvector.com", "basePath": "/", "schemes": [ "https" ], "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json" ], "securityDefinitions": { "token": { "name": "Authorization", "type": "apiKey", "in": "header" } }, "paths": { "/organizations": { "get": { "summary": "List all organizations", "description": "List all organizations", "operationId": "GET--organizations", "responses": { "200": { "description": "", "schema": { "type": "array", "items": { "$ref": "#/definitions/Organization" } } } }, "tags": [ "Public endpoints" ] } }, "/organizations/{id}/ems_agencies": { "get": { "summary": "List all EMS agencies tied to a specific organization", "description": "List all EMS agencies tied to a specific organization", "operationId": "GET--organizations--id--ems_agencies", "responses": { "200": { "description": "", "schema": { "type": "array", "items": { "$ref": "#/definitions/EmsAgency" } } } ... openapi.json ajaxy_ru
  • 8. MAINTAINING OPENAPI SPEC { "swagger": "2.0", "info": { "title": "Flightcall API 2.0", "description": "This document describes HTTP REST JSON API", "version": "2.0.0" }, "host": "api.flightcall.flightvector.com", "basePath": "/", "schemes": [ "https" ], "consumes": [ "application/x-www-form-urlencoded" ], "produces": [ "application/json" ], "securityDefinitions": { "token": { "name": "Authorization", "type": "apiKey", "in": "header" } }, "paths": { "/organizations": { "get": { "summary": "List all organizations", "description": "List all organizations", "operationId": "GET--organizations", "responses": { "200": { "description": "", "schema": { "type": "array", "items": { "$ref": "#/definitions/Organization" } } } }, "tags": [ "Public endpoints" ] } }, "/organizations/{id}/ems_agencies": { "get": { "summary": "List all EMS agencies tied to a specific organization", "description": "List all EMS agencies tied to a specific organization", "operationId": "GET--organizations--id--ems_agencies", "responses": { "200": { "description": "", "schema": { "type": "array", "items": { "$ref": "#/definitions/EmsAgency" } } } ... openapi.json VERBOSE AND BORING ajaxy_ru
  • 9. Attempts to optimize: · Multiple files · JSDoc · Online editors and services MAINTAINING OPENAPI SPEC ajaxy_ru
  • 10. Augmenting this presentation with examples MAINTAINING OPENAPI SPEC TINYSPEC ajaxy_ru
  • 11. MAINTAINING OPENAPI SPEC Imagine, we need to Get users… ajaxy_ru
  • 12. User {name, age?: i, isAdmin: b} user.models.tinyspec MAINTAINING OPENAPI SPEC ajaxy_ru Imagine, we need to Get users…
  • 13. User {name, age?: i, isAdmin: b} user.models.tinyspec GET /users => {users: User[]} users.endpoints.tinyspec MAINTAINING OPENAPI SPEC ajaxy_ru Imagine, we need to Get users…
  • 15. User {name, age?: i, isAdmin: b} GET /users => {users: User[]} user.models.tinyspec users.endpoints.tinyspec { "swagger": "2.0", "info": { "title": "API Example", "description": "API Example", "version": "1.0.0" }, "paths": { "/users": { "get": { "summary": "GET /users", "description": "GET /users", "operationId": "GET--users", "responses": { "200": { "description": "", "schema": { "type": "object", "properties": { "users": { "type": "array", "items": { "$ref": "#/definitions/User" } } }, "required": [ "users" ] } } } } } }, "tags": [], "definitions": { "User": { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" }, "isAdmin": { "type": "boolean" } }, "required": [ "name", "isAdmin" ] } } } openapi.json $ tinyspec -–json MAINTAINING OPENAPI SPEC ajaxy_ru Imagine, we need to Get users…
  • 17. YOU CAN RE-USE SPEC IN CODE! SPECIFICATION-DRIVEN DEVELOPMENT ajaxy_ru
  • 20. 1 · ENDPOINT UNIT TESTS describe('/users', () => { it('List all users', async () => { const { status, body: { users } } = request.get('/users'); expect(users[0].name).to.be('string'); expect(users[0].isAdmin).to.be('boolean'); expect(users[0].age).to.be.oneOf(['boolean', null]); }); }); describe 'GET /users' do it 'List all users' do get '/users' expect_json_types('users.*', { name: :string, isAdmin: :boolean, age: :integer_or_null, }) end end npmjs.com/package/supertest rubygems.org/gems/airborne
  • 21. User {name, age?: i, isAdmin: b} GET /users => {users: User[]} user.models.tinyspec users.endpoints.tinyspec { "swagger": "2.0", "info": { "title": "API Example", "description": "API Example", "version": "1.0.0" }, "paths": { "/users": { "get": { "summary": "GET /users", "description": "GET /users", "operationId": "GET--users", "responses": { "200": { "description": "", "schema": { "type": "object", "properties": { "users": { "type": "array", "items": { "$ref": "#/definitions/User" } } }, "required": [ "users" ] } } } } } }, "tags": [], "definitions": { "User": { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" }, "isAdmin": { "type": "boolean" } }, "required": [ "name", "isAdmin" ] } } } openapi.json $ tinyspec -–json 1 · ENDPOINT UNIT TESTS ajaxy_ru Imagine, we need to Get users…
  • 22. 1 · ENDPOINT UNIT TESTS json-schema.org "User": { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" }, "isAdmin": { "type": "boolean" } }, "required": [ "name", "isAdmin" ] } JSON SCHEMA ajaxy_ru
  • 23. 1 · ENDPOINT UNIT TESTS Any key-value object may be validated against JSON Schema json-schema.org "User": { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" }, "isAdmin": { "type": "boolean" } }, "required": [ "name", "isAdmin" ] } JSON SCHEMA ajaxy_ru
  • 24. npmjs.com/package/jest-ajv rubygems.org/gems/json_matchers npmjs.com/package/chai-ajv-json-schema 1 · ENDPOINT UNIT TESTS Any key-value object may be validated against JSON Schema ajaxy_ru
  • 25. 1 · ENDPOINT UNIT TESTS import deref from 'json-schema-deref-sync'; const schemas = deref(require('./openapi.json')).definitions; describe('/users', () => { it('List all users', async () => { const { status, body: { users } } = request.get('/users'); expect(users[0]).toMatchSchema(schemas.User); }); }); require ‘json_matchers/rspec' JsonMatchers.schema_root = 'spec/schemas' describe 'GET /users' do it 'List all users' do get '/users' expect(result[:users][0]).to match_json_schema('User') end end npmjs.com/package/jest-ajv rubygems.org/gems/json_matchers
  • 27. 2 · USER-DATA VALIDATION User {name, age?: i, isAdmin: b} user.models.tinyspec Imagine, we need to Update a user… ajaxy_ru
  • 28. 2 · USER-DATA VALIDATION User {name, age?: i, isAdmin: b} UserUpdate !{name?, age?: i} user.models.tinyspec Imagine, we need to Update a user… ajaxy_ru
  • 29. 2 · USER-DATA VALIDATION User {name, age?: i, isAdmin: b} UserUpdate !{name?, age?: i} user.models.tinyspec Imagine, we need to Update a user… ajaxy_ru
  • 30. 2 · USER-DATA VALIDATION User {name, age?: i, isAdmin: b} UserUpdate !{name?, age?: i} user.models.tinyspec Imagine, we need to Update a user… ajaxy_ru
  • 31. PATCH /users/:id {user: UserUpdate} => {success: b} users.endpoints.tinyspec 2 · USER-DATA VALIDATION UserUpdate !{name?, age?: i} user.models.tinyspec Imagine, we need to Update a user… ajaxy_ru
  • 32. UserUpdate !{name?, age?: i} PATCH /users/:id {user: UserUpdate} => {success: b} user.models.tinyspec users.endpoints.tinyspec 2 · USER-DATA VALIDATION ... "paths": { "/users/{id}": { "patch": { "summary": "PATCH /users/:id", "description": "PATCH /users/:id", "operationId": "PATCH--users--id", "responses": { "200": { "description": "", "schema": { "type": "object", "properties": { "success": { "type": "boolean" } }, "required": [ "success" ] } } }, "parameters": [ { "name": "id", "type": "string", "in": "path", "required": true }, { "name": "body", "required": true, "schema": { "type": "object", "properties": { "user": { "$ref": "#/definitions/UserUpdate" } }, "required": [ "user" ] }, "in": "body" } ] } } }, "tags": [], "definitions": { "UserUpdate": { "type": "object", "properties": { "name": { "type": "string" }, "age": { "type": "integer" } }, "additionalProperties": false } } openapi.json Imagine, we need to Update a user… $ tinyspec -–json ajaxy_ru
  • 34. router.patch('/:id', async (ctx) => { const updateData = ctx.body.user; // Validation using JSON schema from API specification. await validate(schemas.UserUpdate, updateData); const user = await User.findById(ctx.params.id); await user.update(updateData); ctx.body = { success: true }; }); 2 · USER-DATA VALIDATION ajaxy_ru
  • 35. router.patch('/:id', async (ctx) => { const updateData = ctx.body.user; // Validation using JSON schema from API specification. await validate(schemas.UserUpdate, updateData); const user = await User.findById(ctx.params.id); await user.update(updateData); ctx.body = { success: true }; }); 2 · USER-DATA VALIDATION FieldsValidationError { error: b, message, fields: {name, message}[] } error.models.tinyspec ajaxy_ru
  • 36. router.patch('/:id', async (ctx) => { const updateData = ctx.body.user; // Validation using JSON schema from API specification. await validate(schemas.UserUpdate, updateData); const user = await User.findById(ctx.params.id); await user.update(updateData); ctx.body = { success: true }; }); 2 · USER-DATA VALIDATION FieldsValidationError { error: b, message, fields: {name, message}[] } error.models.tinyspec PATCH /users/:id {user: UserUpdate} => 200 {success: b} => 422 FieldsValidationError users.endpoints.tinyspec ajaxy_ru
  • 38. 3 · MODEL SERIALIZATION {…} DB TABLE JSON VIEWORM MODEL ajaxy_ru
  • 39. {…} ORM MODEL JSON VIEW serialization DB TABLE 3 · MODEL SERIALIZATION ajaxy_ru
  • 41. Our Spec has all needed information! 3 · MODEL SERIALIZATION {…} {…} {…} ALTERNATE REQUESTSajaxy_ru
  • 42. Imagine, we need to Get all users with posts and comments… 3 · MODEL SERIALIZATION ajaxy_ru
  • 43. Comment {authorId: i, message} Post {topic, message, comments?: Comment[]} User {name, isAdmin: b, age?: i} UserWithPosts < User {posts: Post[]} models.tinyspec 3 · MODEL SERIALIZATION ajaxy_ru
  • 44. Comment {authorId: i, message} Post {topic, message, comments?: Comment[]} User {name, isAdmin: b, age?: i} UserWithPosts < User {posts: Post[]} GET /blog/users => {users: UserWithPosts[]} models.tinyspec blogUsers.endpoints.tinyspec 3 · MODEL SERIALIZATION ajaxy_ru
  • 45. Comment {authorId: i, message} Post {topic, message, comments?: Comment[]} User {name, isAdmin: b, age?: i} UserWithPosts < User {posts: Post[]} GET /blog/users => {users: UserWithPosts[]} models.tinyspec blogUsers.endpoints.tinyspec 3 · MODEL SERIALIZATION … »paths»: { "/blog/users": { "get": { "summary": "GET /blog/users", "description": "GET /blog/users", "operationId": "GET--blog--users", "responses": { "200": { "description": "", "schema": { "type": "object", "properties": { "users": { "type": "array", "items": { "$ref": "#/definitions/UserWithPosts" } } }, "required": [ "users" ] } } } } } }, "tags": [], "definitions": { "Comment": { "type": "object", "properties": { "authorId": { "type": "integer" }, "message": { "type": "string" } }, "required": [ "authorId", "message" ] }, "Post": { "type": "object", "properties": { "topic": { "type": "string" }, "message": { "type": "string" }, "comments": { "type": "array", "items": { "$ref": "#/definitions/Comment" } } }, "required": [ "topic", "message" ] }, "User": { "type": "object", "properties": { "name": { "type": "string" }, "isAdmin": { "type": "boolean" }, "age": { "type": "integer" } }, "required": [ "name", "isAdmin" ] }, "UserWithPosts": { "type": "object", "properties": { "name": { "type": "string" }, "isAdmin": { "type": "boolean" }, "age": { "type": "integer" }, "posts": { "type": "array", "items": { "$ref": "#/definitions/Post" } } }, "required": [ "name", "isAdmin", "posts" ] } } openapi.json $ tinyspec -–json ajaxy_ru
  • 46. 3 · MODEL SERIALIZATION npmjs.com/package/sequelize-serialize ajaxy_ru
  • 47. 3 · MODEL SERIALIZATION import serialize from 'sequelize-serialize'; router.get('/blog/users', async (ctx) => { const users = await User.findAll({ include: [{ association: User.posts, include: [ Post.comments ] }] }); ctx.body = serialize(users, schemas.UserWithPosts); }); npmjs.com/package/sequelize-serialize ajaxy_ru
  • 50. 4 · STATIC TYPING JSON SCHEMA ajaxy_ru
  • 52. 4 · STATIC TYPING $ tinyspec -–json $ sw2dts ./openapi.json -o Api.d.ts --namespace Api ajaxy_ru
  • 53. 4 · STATIC TYPING $ tinyspec -–json $ sw2dts ./openapi.json -o Api.d.ts --namespace Api declare namespace Api { export interface Comment { authorId: number; message: string; } export interface Post { topic: string; message: string; comments?: Comment[]; } export interface User { name: string; isAdmin: boolean; age?: number; } export interface UserUpdate { name?: string; age?: number; } export interface UserWithPosts { name: string; isAdmin: boolean; age?: number; posts: Post[]; } } Api.d.ts
  • 54. router.patch('/users/:id', async (ctx) => { // Specify type for request data object const userData: Api.UserUpdate = ctx.request.body.user; // Run spec validation await validate(schemas.UserUpdate, userData); // Query the database const user = await User.findById(ctx.params.id); await user.update(userData); // Return serialized result const serialized: Api.User = serialize(user, schemas.User); ctx.body = { user: serialized }; }); 4 · STATIC TYPING ajaxy_ru
  • 55. it('Update user', async () => { // Static check for test input data. const updateData: Api.UserUpdate = { name: MODIFIED }; const res = await request.patch('/users/1', { user: updateData }); // Type helper for request response: const user: Api.User = res.body.user; expect(user).to.be.validWithSchema(schemas.User); expect(user).to.containSubset(updateData); }); 4 · STATIC TYPING router.patch('/users/:id', async (ctx) => { // Specify type for request data object const userData: Api.UserUpdate = ctx.request.body.user; // Run spec validation await validate(schemas.UserUpdate, userData); // Query the database const user = await User.findById(ctx.params.id); await user.update(userData); // Return serialized result const serialized: Api.User = serialize(user, schemas.User); ctx.body = { user: serialized }; }); ajaxy_ru
  • 57. 5 · TYPE CASTING param1=value&param2=777&param3=false Query params or non-JSON body: ajaxy_ru
  • 58. 5 · TYPE CASTING param1=value&param2=777&param3=false { param1: 'value', param2: '777', param3: 'false' } Query params or non-JSON body: ajaxy_ru
  • 60. import castWithSchema from 'cast-with-schema'; router.get('/posts', async (ctx) => { // Cast parameters to expected types const query = castWithSchema(ctx.query, schemas.PostsQuery); // Run spec validation await validate(schemas.PostsQuery, query); // Query the database const posts = await Post.search(query); // Return serialized result ctx.body = { posts: serialize(posts, schemas.Post) }; }); npmjs.com/package/cast-with-schema 5 · TYPE CASTING ajaxy_ru