SlideShare a Scribd company logo
1 of 61
Download to read offline
Specification-Driven
Development of REST APIs
Alexander

Zinchuk
Summer. Sea. JavaScript.
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]);
});
});
npmjs.com/package/supertest
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);
});
});
npmjs.com/package/jest-ajv
ajaxy_ru
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
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

More Related Content

What's hot

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 collectionJoEllen Carter
 
Heroku pop-behind-the-sense
Heroku pop-behind-the-senseHeroku pop-behind-the-sense
Heroku pop-behind-the-senseBen Lin
 
Angular JS blog tutorial
Angular JS blog tutorialAngular JS blog tutorial
Angular JS blog tutorialClaude Tech
 
Workshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte IWorkshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte IVisual Engineering
 
Test-driven Development with AEM
Test-driven Development with AEMTest-driven Development with AEM
Test-driven Development with AEMJan Wloka
 
Unit Testing at Scale
Unit Testing at ScaleUnit Testing at Scale
Unit Testing at ScaleJan Wloka
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS ArchitectureEyal Vardi
 
PrimeTime JSF with PrimeFaces - Dec 2014
PrimeTime JSF with PrimeFaces - Dec 2014PrimeTime JSF with PrimeFaces - Dec 2014
PrimeTime JSF with PrimeFaces - Dec 2014cagataycivici
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and PythonPiXeL16
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications Juliana Lucena
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenerytoddbr
 
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Jeado Ko
 
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces Skills Matter
 

What's hot (20)

Sane Async Patterns
Sane Async PatternsSane Async Patterns
Sane Async Patterns
 
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
 
Basics of AngularJS
Basics of AngularJSBasics of AngularJS
Basics of AngularJS
 
WCLA12 JavaScript
WCLA12 JavaScriptWCLA12 JavaScript
WCLA12 JavaScript
 
Heroku pop-behind-the-sense
Heroku pop-behind-the-senseHeroku pop-behind-the-sense
Heroku pop-behind-the-sense
 
Angular JS blog tutorial
Angular JS blog tutorialAngular JS blog tutorial
Angular JS blog tutorial
 
Presentation
PresentationPresentation
Presentation
 
Workshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte IWorkshop 12: AngularJS Parte I
Workshop 12: AngularJS Parte I
 
Test-driven Development with AEM
Test-driven Development with AEMTest-driven Development with AEM
Test-driven Development with AEM
 
Unit Testing at Scale
Unit Testing at ScaleUnit Testing at Scale
Unit Testing at Scale
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
 
PrimeTime JSF with PrimeFaces - Dec 2014
PrimeTime JSF with PrimeFaces - Dec 2014PrimeTime JSF with PrimeFaces - Dec 2014
PrimeTime JSF with PrimeFaces - Dec 2014
 
REST with Eve and Python
REST with Eve and PythonREST with Eve and Python
REST with Eve and Python
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
Javascript first-class citizenery
Javascript first-class citizeneryJavascript first-class citizenery
Javascript first-class citizenery
 
AngularJS Basics with Example
AngularJS Basics with ExampleAngularJS Basics with Example
AngularJS Basics with Example
 
Jsp
JspJsp
Jsp
 
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
Angular를 활용한 웹 프론트단 개발과 2.0에서 달라진점
 
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
 
AngularJs Crash Course
AngularJs Crash CourseAngularJs Crash Course
AngularJs Crash Course
 

Similar to Specification-Driven Development of REST APIs by Alexander Zinchuk

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 PresentationJeremy Przygode
 
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 - WorkshopNuxeo
 
Serverless archtiectures
Serverless archtiecturesServerless archtiectures
Serverless archtiecturesIegor Fadieiev
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsFrancois Zaninotto
 
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 - Japantristansokol
 
AngularJS Mobile Warsaw 20-10-2014
AngularJS Mobile Warsaw 20-10-2014AngularJS Mobile Warsaw 20-10-2014
AngularJS Mobile Warsaw 20-10-2014Dariusz Kalbarczyk
 
Developing RESTful WebServices using Jersey
Developing RESTful WebServices using JerseyDeveloping RESTful WebServices using Jersey
Developing RESTful WebServices using Jerseyb_kathir
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesLindsay Holmwood
 
Flask and Angular: An approach to build robust platforms
Flask and Angular:  An approach to build robust platformsFlask and Angular:  An approach to build robust platforms
Flask and Angular: An approach to build robust platformsAyush Sharma
 
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 - TorontoAmazon Web Services
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVCAlive Kuo
 
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 –...Amazon Web Services
 
Azure Day Reloaded 2019 - ARM Template workshop
Azure Day Reloaded 2019 - ARM Template workshopAzure Day Reloaded 2019 - ARM Template workshop
Azure Day Reloaded 2019 - ARM Template workshopMarco Obinu
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js frameworkBen Lin
 
Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013
Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013
Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013Amazon Web Services
 
Couchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problemCouchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problemdelagoya
 

Similar to Specification-Driven Development of REST APIs by Alexander Zinchuk (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
 
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
 
Bonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node jsBonnes pratiques de développement avec Node js
Bonnes pratiques de développement avec Node js
 
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
 
Android and REST
Android and RESTAndroid and REST
Android and REST
 
Os Pruett
Os PruettOs Pruett
Os Pruett
 
AngularJS Mobile Warsaw 20-10-2014
AngularJS Mobile Warsaw 20-10-2014AngularJS Mobile Warsaw 20-10-2014
AngularJS Mobile Warsaw 20-10-2014
 
Developing RESTful WebServices using Jersey
Developing RESTful WebServices using JerseyDeveloping RESTful WebServices using Jersey
Developing RESTful WebServices using Jersey
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
Flask and Angular: An approach to build robust platforms
Flask and Angular:  An approach to build robust platformsFlask and Angular:  An approach to build robust platforms
Flask and Angular: An approach to build robust platforms
 
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
 
infrastructure as code
infrastructure as codeinfrastructure as code
infrastructure as code
 
[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC[Coscup 2012] JavascriptMVC
[Coscup 2012] JavascriptMVC
 
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 –...
 
Serverless Java on Kubernetes
Serverless Java on KubernetesServerless Java on Kubernetes
Serverless Java on Kubernetes
 
Azure Day Reloaded 2019 - ARM Template workshop
Azure Day Reloaded 2019 - ARM Template workshopAzure Day Reloaded 2019 - ARM Template workshop
Azure Day Reloaded 2019 - ARM Template workshop
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013
Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013
Zero to Sixty: AWS CloudFormation (DMG201) | AWS re:Invent 2013
 
Couchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problemCouchdb: No SQL? No driver? No problem
Couchdb: No SQL? No driver? No problem
 

More from OdessaJS Conf

'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021OdessaJS Conf
 
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021OdessaJS Conf
 
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021OdessaJS Conf
 
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...OdessaJS Conf
 
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021OdessaJS Conf
 
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...OdessaJS Conf
 
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...OdessaJS Conf
 
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021OdessaJS Conf
 
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні..."NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...OdessaJS Conf
 
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia NecheporenkoOdessaJS Conf
 
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro GusevOdessaJS Conf
 
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav DvorovenkoOdessaJS Conf
 
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...OdessaJS Conf
 
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
 'MICROFRONTENDS WITH REACT' by Liliia Karpenko 'MICROFRONTENDS WITH REACT' by Liliia Karpenko
'MICROFRONTENDS WITH REACT' by Liliia KarpenkoOdessaJS Conf
 
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020OdessaJS Conf
 
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020OdessaJS Conf
 
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020OdessaJS Conf
 
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020OdessaJS Conf
 
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020OdessaJS Conf
 
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020OdessaJS Conf
 

More from OdessaJS Conf (20)

'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
'GraphQL Schema Design' by Borys Mohyla. OdessaJS'2021
 
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
'How i came up with my talk' by Yurii Artiukh. OdessaJS'2021
 
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
"Is there life in react without redux" by Babich Sergiy. OdessaJS'2021
 
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
Олексій Павленко. CONTRACT PROTECTION ON THE FRONTEND SIDE: HOW TO ORGANIZE R...
 
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
Андрій Троян. Розробка мікросервісів з NestJS. OdessaJS'2021
 
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
Олексій Гончар "Використання Electron в розробці корпоративної відео-мессeндж...
 
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
Максим Климишин "Що такого особливого у пропозиції вартості шаблону Micro Fro...
 
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
Павло Галушко. GOOD CODE MYTHS. OdessaJS'2021
 
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні..."NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
"NODEJS & GRAPHQL COOKBOOK. LET’S TALK ABOUT MICRO-SERVICES" by Антон Чередні...
 
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
'BUILDING ANGULAR APPS WITH NX' by Anastasia Necheporenko
 
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by  Dmytro Gusev
'IS THERE JAVASCRIPT ON SWAGGER PLUGINS?' by Dmytro Gusev
 
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
'ETHEREUM SMART CONTRACTS ON JS' by Yaroslav Dvorovenko
 
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
'GOLANG USAGE IN DEVELOPMENT OF NODE.JS APPLICATIONS (NODE.JS: IN GO WE TRUST...
 
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
 'MICROFRONTENDS WITH REACT' by Liliia Karpenko 'MICROFRONTENDS WITH REACT' by Liliia Karpenko
'MICROFRONTENDS WITH REACT' by Liliia Karpenko
 
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
'Web performance metrics' BY ROMAN SAVITSKYI at OdessaJS'2020
 
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020'STORY OF ANOTHER ANIMATION' by  YURII ARTYUKH at OdessaJS'2020
'STORY OF ANOTHER ANIMATION' by YURII ARTYUKH at OdessaJS'2020
 
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
'JavaScript was invented in Odessa' by DMITRIY GUSEV at OdessaJS'2020
 
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020'Why svelte' by BORYS MOHYLA at OdessaJS'2020
'Why svelte' by BORYS MOHYLA at OdessaJS'2020
 
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
'Effective node.js development' by Viktor Turskyi at OdessaJS'2020
 
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
'Tensorflow.js in real life' by Pavlo Galushko at OdessaJS'2020
 

Recently uploaded

TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 

Recently uploaded (20)

TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 

Specification-Driven Development of REST APIs by Alexander Zinchuk

  • 1. Specification-Driven Development of REST APIs Alexander
 Zinchuk Summer. Sea. JavaScript.
  • 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]); }); }); npmjs.com/package/supertest
  • 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); }); }); npmjs.com/package/jest-ajv ajaxy_ru
  • 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 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