Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
JSON Schema Centralized Design
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
pika_shi
November 26, 2017
Technology
5.2k
5
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
JSON Schema Centralized Design
Node Fest 2017
pika_shi
November 26, 2017
More Decks by pika_shi
See All by pika_shi
「規約に同意」のUX -ストレスフリーな同意UIとその実現方法-
pika_shi
21
15k
Other Decks in Technology
See All in Technology
Databricksメトリクスビューはじめてのもくもく会
taka_aki
0
200
ai_cording_with_k8s_knowledge.pdf
mochizuki875
1
280
全社共通データ基盤をつくる。ソニーのDatabricks活用とデータガバナンス設計の裏側
sony
0
420
AIアプリを使い分けるための共通ツール基盤を考えてみる#devio2026
osawa
0
250
【データ横丁主催】AI Agentがコンテキストを使って仕事をした後、何が残るのか― 組織の経験を次の判断に引き継ぐ「Agent Memory」
shisyu_gaku
2
330
React Nativeでの OTA Updateって、 どう説明する?
ichiki1023
0
150
Deep Data Security 機能解説
oracle4engineer
PRO
2
690
自律型 AI をセキュアに実装!Gemini と MIG で作る動的コード実行環境
recruitengineers
PRO
1
210
LLM機能を自作して分かるSnowflake Cortex AIの強み
nayuts
0
290
Codex概要
ymiya55
0
400
[2026-09-30]ロックンロールは鳴り止まないっ - 信頼性かまってちゃん - 「データ駆動を投げ捨ててまで。」追いかける信頼性改善に向けた取り組みの話
tosite
0
200
freeeらしさをAIとともに作る / Creating the freee Experience with AI
ymrl
0
200
Featured
See All Featured
Scaling GitHub
holman
464
140k
Designing Powerful Visuals for Engaging Learning
tmiket
1
590
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
1
550
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
360
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
66k
WCS-LA-2024
lcolladotor
0
840
sira's awesome portfolio website redesign presentation
elsirapls
0
440
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
300
A designer walks into a library…
pauljervisheath
211
25k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
620
Transcript
JSON Schema Centralized Design Node Fest Tokyo 2017 (2017/11/26) @pika_shi
- Hikaru Takemura (JSON Schema த৺ઃܭ)
‣ Hikaru Takemura ‣ @pika_shi ‣ FOLIO ‣ Frontend Engineer
(React, Node) ‣ AdriaBlue ‣ Mobile App Developer (SwiA)
API Specifica<on
API Specifica8on ‣ PROS ‣ API ఆٛΛ໌จԽ͓ͯ͘͜͠ͱͰɼϑϩϯτŋόοΫؒͰ ࣮ΛεϜʔζʹਐΊΒΕΔ ‣ body
ͷܕఆٛΛݫີʹ͓͜ͳ͏͜ͱ͕Ͱ͖Δ ‣ ੬ऑੑஅͰͷ URL εΩϟϯͷࡍʹར༻Ͱ͖Δ
API Specifica8on ‣ CONS ‣ ༷ͱ࣮͕ঃʑʹဃ͍ͯ͘͠ ‣ ͦͷ݁Ռɼࢀর͖͢ใ͕͔ΒͣɼMicroservices ؒŋϓϩδΣΫτͰίϛϡχέʔγϣϯʹᴥᴪ͕ੜ͡Δ Service
Service Service Service API Spec
Mo8va8on ‣ ༷ͱ࣮͕ဃ͢ΔͷɼͦΕΒ͕ಠཱʹϝϯςφϯε ͞ΕΔ͔Β ‣ ϝϯςφϯε͢ΔͷΛ 1 ͭʹ͠ɼ༷ŋؚ࣮Ίͯͦ͢ ͷใΛࢀর͢ΔΑ͏ʹ͍ͨ͠ Service
Service Service Service API Spec
JSON Schema & RAML
JSON Schema ‣ JSON Object ͷܕఆٛϑΥʔϚοτ ‣ JSON Ͱهड़ (YAML
Ͱهड़͢Δ߹͕ଟ͍) ‣ minimum, maximum ͔Βɼਖ਼نදݱΛѻ͑Δ paLern ·Ͱɼ༷ʑͳϓϩύςΟ͕ఆٛ͞Ε͍ͯΔ
JSON Schema ‣ JSON Object ͷܕఆٛϑΥʔϚοτ --- $schema: hLp:/ /json-schema.org/draA-04/schema#
id: user type: object required: - id - name - state properUes: id: descripUon: user id type: number name: descripUon: user's name type: string state: descripUon: user state type: number enum: - 1 # acUve - 2 # inacUve addiUonalProperUes: false { "id": 23, "name": “John Due”, "state": 2 } { "id": “23”, "name": “John Due”, "state": 3, “phone”: “+819000000000” } ◦ × user.yml
RAML ‣ REST API ఆٛϑΥʔϚοτ ‣ YAML Ͱهड़ ‣ ༷ΛόʔδϣϯཧͰ͖ɼมߋ
diff ͰཧͰ͖Δ ‣ JSON Schema Λ include Ͱ͖Δ ‣ ڞ௨෦ΛఆٛͰ͖ɼ࠶ར༻͍͢͠ ‣ ଞʹ Swagger, Open API, API Blueprint ͕͋Δ
RAML ‣ REST API ఆٛϑΥʔϚοτ #%RAML 0.8 Utle: User version:
v1.0 schemas: - User: !include user.json # ͖ͬ͞ͷ JSON Schema /user: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβใΛऔಘ responses: 200: descripUon: Ϣʔβͷใ͕औಘͰ͖ͨ߹ body: applicaUon/json: schema: User 404: descripUon: Ϣʔβใ͕ଘࡏ͠ͳ͍߹ user.raml
RAML ‣ REST API ఆٛϑΥʔϚοτ user.raml #%RAML 0.8 Utle: User
version: v1.0 schemas: - User: !include user.json # ͖ͬ͞ͷ JSON Schema /user: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβใΛऔಘ responses: 200: descripUon: Ϣʔβͷใ͕औಘͰ͖ͨ߹ body: applicaUon/json: schema: User 404: descripUon: Ϣʔβใ͕ଘࡏ͠ͳ͍߹
JSON Schema Centralized Design (JSON Schema த৺ઃܭ)
‣ JSON Schema ͱ RAML Λத৺ʹਾ͑ͨΤίγεςϜΛߏங JSON Schema Centralized Design
JSON Schema RAML include API Document URL Λ JS ͷ มͱͯ͠ఆٛ Valida<on FlowType Stub Object
‣ JSON Schema ͱ RAML Λத৺ʹਾ͑ͨΤίγεςϜΛߏங JSON Schema Centralized Design
JSON Schema RAML include API Document URL Λ JS ͷ มͱͯ͠ఆٛ Valida<on FlowType Stub Object ϝϯςφϯε͢Δͷ͜͜ͷΈ
API Document URL Λ JS ͷ มͱͯ͠ఆٛ ① API Document
include JSON Schema RAML Valida<on FlowType Stub Object
① API Document ‣ raml2html Ͱ RAML ͔Β API υΩϡϝϯτΛੜ
‣ γϯϓϧ͔ͭΠϯλϥΫςΟϒͳ HTML υΩϡϝϯτ
‣ raml2html Ͱ RAML ͔Β API υΩϡϝϯτΛੜ ‣ γϯϓϧ͔ͭΠϯλϥΫςΟϒͳ HTML
υΩϡϝϯτ ① API Document JSON Schema
URL Λ JS ͷ มͱͯ͠ఆٛ API Document ② URL Λ
JS ͷมͱͯ͠ఆٛ include JSON Schema RAML Valida<on FlowType Stub Object
② URL Λ JS ͷมͱͯ͠ఆٛ ‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ ‣ RAML
͔Β JS ͷ URL มఆٛϑΝΠϧΛੜ #%RAML 0.8 Utle: User version: v1.0 schemas: - User: !include user.json /users: get: descripUon: ϢʔβҰཡϖʔδ … /user: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβͷϖʔδ … / / @flow type URLType = { [string]: { [string]: string } } export const userUrl: URLType = { page: { /** * ϢʔβҰཡϖʔδ */ users: ‘/users', /** * ֘͢Δ id ͷϢʔβͷϖʔδ */ user: ‘/user/{user_id}' } } user.raml urls.js
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ urls.js template RAML ② URL Λ JS
ͷมͱͯ͠ఆٛ generator urls.js frontend backend …
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … #%RAML 0.8 Utle: User version: v1.0 schemas: - User: !include user.json /users: get: descripUon: ϢʔβҰཡϖʔδ displayName: page-users-get … /user: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβͷϖʔδ displayName: page-users-get … user.raml urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … #%RAML 0.8 Utle: User version: v1.0 schemas: - User: !include user.json /users: get: descripUon: ϢʔβҰཡϖʔδ displayName: page-users-get … /user: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβͷϖʔδ displayName: page-users-get … urls.tpl.js ʹ inject ͢ΔͨΊͷ key ͱͯ͠ར༻ user.raml urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ urls.js template RAML ② URL Λ JS
ͷมͱͯ͠ఆٛ generator urls.js frontend backend … / / @flow type URLType = { [string]: { [string]: string } } export const userUrl: URLType = { page: { /** * {{page-users-get.descripUon}} */ users: ‘{{page-users-get.url}}’, /** * {{page-user-user_id-get.descripUon}} */ user: ‘{{page-user-user_id-get.url}}’ } } urls.tpl.js
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … RAML ͷ displayName (key) ͔Β url descrip<on Λ inject / / @flow type URLType = { [string]: { [string]: string } } export const userUrl: URLType = { page: { /** * {{page-users-get.descripUon}} */ users: ‘{{page-users-get.url}}’, /** * {{page-user-user_id-get.descripUon}} */ user: ‘{{page-user-user_id-get.url}}’ } } urls.tpl.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … const fs = require('fs') const ramlParser = require('raml-parser') const handlebars = require('handlebars') const urlMap = {} const setUrlFromRaml = (data, prefix = '') => { data.resources.forEach(resource => { if (resource.methods) { if (urlMap[resource.methods[0].displayName]) { throw new Error('Duplicated displayName') } urlMap[resource.methods[0].displayName] = { url: `${prefix}${resource.relaUveUri}`, descripUon: resource.methods[0].descripUon } } else { setUrlFromRaml(resource, `${prefix}${resource.relaUveUri}`) } }) } ramlParser.loadFile(‘page.raml').then(data => { try { setUrlFromRaml(data) } catch(_) { process.exit(1) } console.log(handlebars.compile(fs.readFileSync('urls.tpl.js', 'us8'))(urlMap)) }) generator.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … RAML Λύʔε const fs = require('fs') const ramlParser = require('raml-parser') const handlebars = require('handlebars') const urlMap = {} const setUrlFromRaml = (data, prefix = '') => { data.resources.forEach(resource => { if (resource.methods) { if (urlMap[resource.methods[0].displayName]) { throw new Error('Duplicated displayName') } urlMap[resource.methods[0].displayName] = { url: `${prefix}${resource.relaUveUri}`, descripUon: resource.methods[0].descripUon } } else { setUrlFromRaml(resource, `${prefix}${resource.relaUveUri}`) } }) } ramlParser.loadFile(‘page.raml').then(data => { try { setUrlFromRaml(data) } catch(_) { process.exit(1) } console.log(handlebars.compile(fs.readFileSync('urls.tpl.js', 'us8'))(urlMap)) }) generator.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … displayName Λ key ͱͨ͠ Ϛοϐϯά object Λੜ const fs = require('fs') const ramlParser = require('raml-parser') const handlebars = require('handlebars') const urlMap = {} const setUrlFromRaml = (data, prefix = '') => { data.resources.forEach(resource => { if (resource.methods) { if (urlMap[resource.methods[0].displayName]) { throw new Error('Duplicated displayName') } urlMap[resource.methods[0].displayName] = { url: `${prefix}${resource.relaUveUri}`, descripUon: resource.methods[0].descripUon } } else { setUrlFromRaml(resource, `${prefix}${resource.relaUveUri}`) } }) } ramlParser.loadFile(‘page.raml').then(data => { try { setUrlFromRaml(data) } catch(_) { process.exit(1) } console.log(handlebars.compile(fs.readFileSync('urls.tpl.js', 'us8'))(urlMap)) }) generator.js urls.js template { “page-users-get”: { “url”: “/users”, “descripUon”: “ϢʔβҰཡϖʔδ” }, “page-user-get”: { “url”: “/users/{user_id}”, “descripUon”: “֘͢Δ id ͷϢʔβͷϖʔδ” }, }
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … generator.js handlebars ͰςϯϓϨʔτʹ Ϛοϐϯά object Λ inject const fs = require('fs') const ramlParser = require('raml-parser') const handlebars = require('handlebars') const urlMap = {} const setUrlFromRaml = (data, prefix = '') => { data.resources.forEach(resource => { if (resource.methods) { if (urlMap[resource.methods[0].displayName]) { throw new Error('Duplicated displayName') } urlMap[resource.methods[0].displayName] = { url: `${prefix}${resource.relaUveUri}`, descripUon: resource.methods[0].descripUon } } else { setUrlFromRaml(resource, `${prefix}${resource.relaUveUri}`) } }) } ramlParser.loadFile(‘page.raml').then(data => { try { setUrlFromRaml(data) } catch(_) { process.exit(1) } console.log(handlebars.compile(fs.readFileSync('urls.tpl.js', 'us8'))(urlMap)) }) urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … / / @flow type URLType = { [string]: { [string]: string } } export const userUrl: URLType = { page: { /** * {{page-users-get.descripUon}} */ users: ‘{{page-users-get.url}}’, /** * {{page-user-user_id-get.descripUon}} */ user: ‘{{page-user-user_id-get.url}}’ } } urls.tpl.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … / / @flow type URLType = { [string]: { [string]: string } } export const userUrl: URLType = { page: { /** * ϢʔβҰཡϖʔδ */ users: ‘/users’, /** * ֘͢Δ id ͷϢʔβͷϖʔδ */ user: ‘/user/{user_id}’ } } urls.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … import format from 'string-format' import axios from 'axios' import { userUrl } from 'urls' /** * VDOM ͷϨϯμϦϯά */ export const UseLink = ({ user }) => ( <a href={format(userUrl.page.user, { user_id: user.id })}> {user.name} </a> ) /** * API ϦΫΤετ */ export const fetchUserById = user_id => { return dispatch => { axios.get(format(userUrl.api.user, { user_id })) .then(response => /* ... */) .catch(error => /* ... */) } } > const format = require(‘string-format’) undefined > format(‘/user/{user_id}’, { user_id: 1 }) `/user/1` front.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … ‘/user/1’ ͷΑ͏ʹల։͞ΕΔ > const format = require(‘string-format’) undefined > format(‘/user/{user_id}’, { user_id: 1 }) `/user/1` import format from 'string-format' import axios from 'axios' import { userUrl } from 'urls' /** * VDOM ͷϨϯμϦϯά */ export const UseLink = ({ user }) => ( <a href={format(userUrl.page.user, { user_id: user.id })}> {user.name} </a> ) /** * API ϦΫΤετ */ export const fetchUserById = user_id => { return dispatch => { axios.get(format(userUrl.api.user, { user_id })) .then(response => /* ... */) .catch(error => /* ... */) } } front.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js frontend backend … import format from 'string-format' import Router from 'koa-router' import { userUrl } from 'urls' const router = new Router() router.get( userPageUrl.site.users, (ctx, next) => { /* ... */ } ) router.get( format(userUrl.page.user, { user_id: ':id' }), / / '/user/:id' (ctx, next) => { / / this.params.id Ͱ id Λऔಘ } ) back.js urls.js template
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js … frontend ‣ RAML ͷ༷Λ1ߦมߋ͢Δ͚ͩͰɼαʔ όŋΫϥΠΞϯτͷίʔυΛҰมߋ͢Δ ͜ͱͳ͠ʹΤϯυϙΠϯτ໊ΛมߋͰ͖Δ #%RAML 0.8 Utle: User version: v1.0 schemas: - User: !include user.json /user: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβͷϖʔδ displayName: page-user-user_id-get … user.raml urls.js template backend
‣ RAML ͷఆٛΛίʔυͰར༻͠ɼ༷ŋ࣮ؒΛಉظ RAML ② URL Λ JS ͷมͱͯ͠ఆٛ generator
urls.js … frontend ‣ RAML ͷ༷Λ1ߦมߋ͢Δ͚ͩͰɼαʔ όŋΫϥΠΞϯτͷίʔυΛҰมߋ͢Δ ͜ͱͳ͠ʹΤϯυϙΠϯτ໊ΛมߋͰ͖Δ #%RAML 0.8 Utle: User version: v1.0 schemas: - User: !include user.json /customer: /{user_id}: uriParameters: user_id: type: number get: descripUon: ֘͢Δ id ͷϢʔβͷϖʔδ displayName: page-user-user_id-get … user.raml urls.js template backend
API Document ③ Valida8on include JSON Schema RAML Valida<on FlowType
Stub Object URL Λ JS ͷ มͱͯ͠ఆٛ
③ Valida8on ‣ is-my-json-valid Ͱ JSON Object ͷϑΥʔϚοτݕূ ‣ ෆਖ਼ϦΫΤετ
API ༷มߋͳͲʹ؆୯ʹؾ͚ͮΔ
③ Valida8on ‣ is-my-json-valid Ͱ JSON Object ͷϑΥʔϚοτݕূ import fs
from 'fs' import path from 'path' import yaml from 'js-yaml' import isMyJsonValid from 'is-my-json-valid' import assert from 'assert' / / Schema ϑΝΠϧͷಡΈࠐΈ const schemaFilePath = path.resolve(__dirname, 'user.yaml') const schema = yaml.safeLoad( fs.readFileSync(schemaFilePath, 'us8'), { schema: yaml.JSON_SCHEMA } ) const userData = { id: 1, name: ‘Yamada Taro’, state: 1 } / / σʔλ͕ఆ͍ͯ͠Δ Format ͔Ͳ͏͔ݕূ const validator = isMyJsonValid(schema) assert(validator(userData), validator.errors) / / ͦͷޙͷॲཧ / / ... --- $schema: hLp:/ /json-schema.org/draA-04/schema# id: user type: object required: - id - name - state properUes: id: descripUon: user id type: number name: descripUon: user's name type: string state: descripUon: user state type: number enum: - 1 # acUve - 2 # inacUve addiUonalProperUes: false validator.js user.yml
③ Valida8on ‣ is-my-json-valid Ͱ JSON Object ͷϑΥʔϚοτݕূ ‣ addi8onalProper8es
‣ ະఆٛͷϓϩύςΟΛड͚͚Δ͔Ͳ͏͔ ‣ maxItems, minItems, uniqueItems ‣ array ͷཁૉͷ࠷େ, ࠷খ, ϢχʔΫੑ ‣ maximum, minimum ‣ number ͷ࠷େ, ࠷খ ‣ maxLength, minLength ‣ string ͷ ࠷େจࣈ, ࠷খจࣈ ‣ paOern ‣ ਖ਼نදݱ ‣ allOf, oneOf, anyOf, not ‣ schema ͷ AND, OR, NOT --- $schema: hLp:/ /json-schema.org/draA-04/schema# id: user type: object required: - id - name - state properUes: id: descripUon: user id type: number name: descripUon: user's name type: string state: descripUon: user state type: number enum: - 1 # acUve - 2 # inacUve addiUonalProperUes: false user.yml
③ Valida8on ‣ is-my-json-valid Ͱ JSON Object ͷϑΥʔϚοτݕূ frontend backend
(node) import import ϑΥʔϜͷ όϦσʔγϣϯ ϦΫΤετϘσΟͷ λϒϧνΣοΫ import fs from 'fs' import path from 'path' import yaml from 'js-yaml' import isMyJsonValid from 'is-my-json-valid' import assert from 'assert' / / Schema ϑΝΠϧͷಡΈࠐΈ const schemaFilePath = path.resolve(__dirname, 'user.yaml') const schema = yaml.safeLoad( fs.readFileSync(schemaFilePath, 'us8'), { schema: yaml.JSON_SCHEMA } ) const userData = { id: 1, name: ‘Yamada Taro’, state: 1 } / / σʔλ͕ఆ͍ͯ͠Δ Format ͔Ͳ͏͔ݕূ const validator = isMyJsonValid(schema) assert(validator(userData), validator.errors) / / ͦͷޙͷॲཧ / / ... validator.js
③ Valida8on ‣ is-my-json-valid Ͱ JSON Object ͷϑΥʔϚοτݕূ frontend import
import ϑΥʔϜͷ όϦσʔγϣϯ ϦΫΤετϘσΟͷ λϒϧνΣοΫ αʔόŋΫϥΠΞϯτؒͰ ဃ͢Δ͜ͱͳ͘ Ξοϓσʔτ͍ͯ͘͜͠ͱ͕Ͱ͖Δ import fs from 'fs' import path from 'path' import yaml from 'js-yaml' import isMyJsonValid from 'is-my-json-valid' import assert from 'assert' / / Schema ϑΝΠϧͷಡΈࠐΈ const schemaFilePath = path.resolve(__dirname, 'user.yaml') const schema = yaml.safeLoad( fs.readFileSync(schemaFilePath, 'us8'), { schema: yaml.JSON_SCHEMA } ) const userData = { id: 1, name: ‘Yamada Taro’, state: 1 } / / σʔλ͕ఆ͍ͯ͠Δ Format ͔Ͳ͏͔ݕূ const validator = isMyJsonValid(schema) assert(validator(userData), validator.errors) / / ͦͷޙͷॲཧ / / ... validator.js backend (node)
API Document include JSON Schema RAML FlowType Stub Object URL
Λ JS ͷ มͱͯ͠ఆٛ Valida<on ④ FlowType
④ FlowType ‣ json-schema-to-flow-type ͰɼJSON Schema ͔Β FlowType Λࣗಈੜ ‣
ಉ͡Α͏ͳΦϒδΣΫτͷೋॏఆ͕ٛͳ͘ͳΔ /* @flow */ export type User = { id: number; name: string; state: 1 | 2; }; import path from 'path' import yaml from 'js-yaml' import { parseSchema } from 'json-schema-to-flow-type' / / Schema ϑΝΠϧͷಡΈࠐΈ const schemaFilePath = path.resolve(__dirname, 'user.yaml') const schema = yaml.safeLoad( fs.readFileSync(schemaFilePath, 'us8'), { schema: yaml.JSON_SCHEMA } ) console.log(`/* @flow */\n\n${parseSchema(schema)}`) json-schema-to-flow.js user-type.js
‣ REST Ҏ֎Ͱ༷ʑͳ IDL ͔Β FlowType Λੜ͢Δڥ ͖͍ͬͯͯΔͷͰɼࣗͰ FlowType ۃྗॻ͔ͳ͍
④ FlowType protobuf thriW protobuf2flowtype thriW2flow user-type.js /* @flow */ export type User = { id: number; name: string; state: 1 | 2; };
API Document include JSON Schema RAML FlowType Stub Object URL
Λ JS ͷ มͱͯ͠ఆٛ Valida<on ⑤ Stub Object
⑤ Stub Object ‣ json-schema-faker Ͱ JSON Schema ͔ΒελϒΦϒδΣΫ τΛࣗಈੜ
‣ ϑϩϯτŋόοΫؒͷεΩʔϚͷΠϯλϑΣʔεͷΈΛ ઌʹఆ͓ٛͯ͘͜͠ͱͰɼαʔό࣮ŋΫϥΠΞϯτ࣮ Λฒߦͯ͠ਐΊΔ͜ͱ͕Ͱ͖Δ ‣ ςετ༰қʹ
⑤ Stub Object ‣ json-schema-faker Ͱ JSON Schema ͔ΒελϒΦϒδΣΫ τΛࣗಈੜ
const fs = require('fs') const path = require('path') const yaml = require('js-yaml') const jsf = require('json-schema-faker') / / Schema ϑΝΠϧͷಡΈࠐΈ const schemaFilePath = path.resolve(__dirname, 'user.yaml') const schema = yaml.safeLoad( fs.readFileSync(schemaFilePath, 'us8'), { schema: yaml.JSON_SCHEMA } ) jsf.resolve(schema).then(result => console.log(JSON.stringify(result, null, 2)) ) { "id": 23, "name": “John Due”, "state": 2 } --- $schema: hLp:/ /json-schema.org/draA-04/schema# id: user type: object required: - id - name - state properUes: id: descripUon: user id type: number faker: random.number name: descripUon: user's name type: string faker: user.findName state: descripUon: user state type: number enum: - 1 # acUve - 2 # inacUve addiUonalProperUes: false user.yml generate-stub.js
⑤ Stub Object ‣ json-schema-faker Ͱ JSON Schema ͔ΒελϒΦϒδΣΫ τΛࣗಈੜ
{ "id": 23, "name": “John Due”, "state": 2 } --- $schema: hLp:/ /json-schema.org/draA-04/schema# id: user type: object required: - id - name - state properUes: id: descripUon: user id type: number faker: random.number name: descripUon: user's name type: string faker: user.findName state: descripUon: user state type: number enum: - 1 # acUve - 2 # inacUve addiUonalProperUes: false const fs = require('fs') const path = require('path') const yaml = require('js-yaml') const jsf = require('json-schema-faker') / / Schema ϑΝΠϧͷಡΈࠐΈ const schemaFilePath = path.resolve(__dirname, 'user.yaml') const schema = yaml.safeLoad( fs.readFileSync(schemaFilePath, 'us8'), { schema: yaml.JSON_SCHEMA } ) jsf.resolve(schema).then(result => console.log(JSON.stringify(result, null, 2)) ) user.yml generate-stub.js
⑤ Stub Object ‣ json-schema-faker Ͱ JSON Schema ͔ΒελϒΦϒδΣΫ τΛࣗಈੜ
‣ ༷ʑͳ face data Λੜ͢Δ͜ͱ͕Ͱ͖Δ ‣ finance ‣ account, amount, bitcoinAddress, … ‣ internet ‣ email, userName, password, … ‣ commerce ‣ productName, price, color, … ‣address ‣ city, zipcode, streetname, … ‣ system ‣ fileName, fileType, filePath, … --- $schema: hLp:/ /json-schema.org/draA-04/schema# id: user type: object required: - id - name - state properUes: id: descripUon: user id type: number faker: random.number name: descripUon: user's name type: string faker: user.findName state: descripUon: user state type: number enum: - 1 # acUve - 2 # inacUve addiUonalProperUes: false user.yml
⑤ Stub Object ‣ raml-server Ͱ mock API Λੜ͢Δ͜ͱͰ͖Δ ‣
JSON Schema ͷ include Մೳ $ raml-server user.raml Running RAML server on localhost:3000... $ curl hLp:/ /localhost:3000/user/1 { "id": 23, "name": “quam esse consectetur”, "state": 2 }
Conclusion
‣ JSON Schema ͱ RAML Λϝϯςφϯε͍ͯ͘͜͠ͱͰɼ API ༷Λ҆શ & ༰қʹΞοϓσʔτ͍ͯ͘͜͠ͱ͕Ͱ͖Δ
Conclusion JSON Schema RAML include API Document URL Λ JS ͷ มͱͯ͠ఆٛ Valida<on FlowType Stub Object
Conclusion Service Service Service Service API Spec v1.0 v1.0 v1.0
v1.0 v1.0 ‣ JSON Schema ͱ RAML Λϝϯςφϯε͍ͯ͘͜͠ͱͰɼ API ༷Λ҆શ & ༰қʹΞοϓσʔτ͍ͯ͘͜͠ͱ͕Ͱ͖Δ ‣ ٯʹݴ͏ͱɼAPI ༷ΛΞοϓσʔτ͠ͳ͍ͱɼ࣮ʹมߋ ΛՃ͑Δ͜ͱ͕Ͱ͖ͳ͍ (SpecificaUon Driven Development) ‣ ݫີ͕͞ॊೈ͞ΛੜΈग़͢