Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JSON Schema Centralized Design

pika_shi
November 26, 2017

JSON Schema Centralized Design

Node Fest 2017

pika_shi

November 26, 2017
Tweet

More Decks by pika_shi

Other Decks in Technology

Transcript

  1. JSON Schema
    Centralized Design
    Node Fest Tokyo 2017 (2017/11/26)
    @pika_shi - Hikaru Takemura
    (JSON Schema த৺ઃܭ)

    View Slide

  2. ‣ Hikaru Takemura
    ‣ @pika_shi
    ‣ FOLIO
    ‣ Frontend Engineer (React, Node)
    ‣ AdriaBlue
    ‣ Mobile App Developer (SwiA)

    View Slide

  3. API Specifica

    View Slide

  4. API Specifica8on
    ‣ PROS
    ‣ API ఆٛΛ໌จԽ͓ͯ͘͜͠ͱͰɼϑϩϯτŋόοΫؒͰ
    ࣮૷ΛεϜʔζʹਐΊΒΕΔ
    ‣ body ͷܕఆٛΛݫີʹ͓͜ͳ͏͜ͱ͕Ͱ͖Δ
    ‣ ੬ऑੑ਍அ౳Ͱͷ URL εΩϟϯͷࡍʹར༻Ͱ͖Δ

    View Slide

  5. API Specifica8on
    ‣ CONS
    ‣ ࢓༷ͱ࣮૷͕ঃʑʹဃ཭͍ͯ͘͠
    ‣ ͦͷ݁Ռɼࢀর͢΂͖৘ใ͕෼͔ΒͣɼMicroservices
    ؒŋϓϩδΣΫτ಺Ͱίϛϡχέʔγϣϯʹᴥᴪ͕ੜ͡Δ
    Service Service Service Service
    API Spec

    View Slide

  6. Mo8va8on
    ‣ ࢓༷ͱ࣮૷͕ဃ཭͢Δͷ͸ɼͦΕΒ͕ಠཱʹϝϯςφϯε
    ͞ΕΔ͔Β
    ‣ ϝϯςφϯε͢Δ΋ͷΛ 1 ͭʹ͠ɼ࢓༷ŋ࣮૷ؚΊ͢΂ͯͦ
    ͷ৘ใΛࢀর͢ΔΑ͏ʹ͍ͨ͠
    Service Service Service Service
    API Spec

    View Slide

  7. JSON Schema & RAML

    View Slide

  8. JSON Schema
    ‣ JSON Object ͷܕఆٛϑΥʔϚοτ
    ‣ JSON Ͱهड़ (YAML Ͱهड़͢Δ৔߹͕ଟ͍)
    ‣ minimum, maximum ͔Βɼਖ਼نදݱΛѻ͑Δ paLern
    ·Ͱɼ༷ʑͳϓϩύςΟ͕ఆٛ͞Ε͍ͯΔ

    View Slide

  9. 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

    View Slide

  10. RAML
    ‣ REST API ఆٛϑΥʔϚοτ
    ‣ YAML Ͱهड़
    ‣ ࢓༷Λόʔδϣϯ؅ཧͰ͖ɼมߋ΋ diff Ͱ؅ཧͰ͖Δ
    ‣ JSON Schema Λ include Ͱ͖Δ
    ‣ ڞ௨෦෼ΛఆٛͰ͖ɼ࠶ར༻͠΍͍͢
    ‣ ଞʹ΋ Swagger, Open API, API Blueprint ౳͕͋Δ

    View Slide

  11. 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

    View Slide

  12. 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: Ϣʔβ৘ใ͕ଘࡏ͠ͳ͍৔߹

    View Slide

  13. JSON Schema
    Centralized Design
    (JSON Schema த৺ઃܭ)

    View Slide

  14. ‣ JSON Schema ͱ RAML Λத৺ʹਾ͑ͨΤίγεςϜΛߏங
    JSON Schema Centralized Design
    JSON
    Schema RAML
    include
    API Document
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    ValidaFlowType
    Stub Object

    View Slide

  15. ‣ JSON Schema ͱ RAML Λத৺ʹਾ͑ͨΤίγεςϜΛߏங
    JSON Schema Centralized Design
    JSON
    Schema RAML
    include
    API Document
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    ValidaFlowType
    Stub Object
    ϝϯςφϯε͢Δͷ͸͜͜ͷΈ

    View Slide

  16. API Document
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    ① API Document
    include
    JSON
    Schema RAML
    ValidaFlowType
    Stub Object

    View Slide

  17. ① API Document
    ‣ raml2html Ͱ RAML ͔Β API υΩϡϝϯτΛੜ੒
    ‣ γϯϓϧ͔ͭΠϯλϥΫςΟϒͳ HTML υΩϡϝϯτ

    View Slide

  18. ‣ raml2html Ͱ RAML ͔Β API υΩϡϝϯτΛੜ੒
    ‣ γϯϓϧ͔ͭΠϯλϥΫςΟϒͳ HTML υΩϡϝϯτ
    ① API Document
    JSON Schema

    View Slide

  19. URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    API Document
    ② URL Λ JS ͷม਺ͱͯ͠ఆٛ
    include
    JSON
    Schema RAML
    ValidaFlowType
    Stub Object

    View Slide

  20. ② 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

    View Slide

  21. ‣ RAML ͷఆٛΛίʔυͰ΋ར༻͠ɼ࢓༷ŋ࣮૷ؒΛಉظ
    urls.js template
    RAML
    ② URL Λ JS ͷม਺ͱͯ͠ఆٛ
    generator
    urls.js
    frontend backend

    View Slide

  22. ‣ 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

    View Slide

  23. ‣ 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

    View Slide

  24. ‣ 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

    View Slide

  25. ‣ RAML ͷఆٛΛίʔυͰ΋ར༻͠ɼ࢓༷ŋ࣮૷ؒΛಉظ
    RAML
    ② URL Λ JS ͷม਺ͱͯ͠ఆٛ
    generator
    urls.js
    frontend backend

    RAML ͷ displayName (key) ͔Β
    url ΍ descrip/
    / @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

    View Slide

  26. ‣ 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

    View Slide

  27. ‣ 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

    View Slide

  28. ‣ 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 ͷϢʔβͷϖʔδ”
    },
    }

    View Slide

  29. ‣ 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

    View Slide

  30. ‣ 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

    View Slide

  31. ‣ 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

    View Slide

  32. ‣ 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 }) => (

    {user.name}

    )
    /**
    * 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

    View Slide

  33. ‣ 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 }) => (

    {user.name}

    )
    /**
    * 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

    View Slide

  34. ‣ 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

    View Slide

  35. ‣ 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

    View Slide

  36. ‣ 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

    View Slide

  37. API Document
    ③ Valida8on
    include
    JSON
    Schema RAML
    ValidaFlowType
    Stub Object
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ

    View Slide

  38. ③ Valida8on
    ‣ is-my-json-valid Ͱ JSON Object ͷϑΥʔϚοτݕূ
    ‣ ෆਖ਼ϦΫΤετ΍ API ࢓༷มߋͳͲʹ؆୯ʹؾ͚ͮΔ

    View Slide

  39. ③ 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

    View Slide

  40. ③ 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

    View Slide

  41. ③ 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

    View Slide

  42. ③ 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)

    View Slide

  43. API Document
    include
    JSON
    Schema RAML
    FlowType
    Stub Object
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    Valida④ FlowType

    View Slide

  44. ④ 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

    View Slide

  45. ‣ REST Ҏ֎Ͱ΋༷ʑͳ IDL ͔Β FlowType Λੜ੒͢Δ؀ڥ͸
    ੔͖͍ͬͯͯΔͷͰɼࣗ෼Ͱ FlowType ͸ۃྗॻ͔ͳ͍
    ④ FlowType
    protobuf
    thriW
    protobuf2flowtype
    thriW2flow
    user-type.js
    /* @flow */
    export type User = {
    id: number;
    name: string;
    state: 1 | 2;
    };

    View Slide

  46. API Document
    include
    JSON
    Schema RAML
    FlowType
    Stub Object
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    Valida⑤ Stub Object

    View Slide

  47. ⑤ Stub Object
    ‣ json-schema-faker Ͱ JSON Schema ͔ΒελϒΦϒδΣΫ
    τΛࣗಈੜ੒
    ‣ ϑϩϯτŋόοΫؒͷεΩʔϚͷΠϯλϑΣʔεͷΈΛ
    ઌʹఆ͓ٛͯ͘͜͠ͱͰɼαʔό࣮૷ŋΫϥΠΞϯτ࣮૷
    Λฒߦͯ͠ਐΊΔ͜ͱ͕Ͱ͖Δ
    ‣ ςετ΋༰қʹ

    View Slide

  48. ⑤ 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

    View Slide

  49. ⑤ 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

    View Slide

  50. ⑤ 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

    View Slide

  51. ⑤ 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
    }

    View Slide

  52. Conclusion

    View Slide

  53. ‣ JSON Schema ͱ RAML Λϝϯςφϯε͍ͯ͘͜͠ͱͰɼ
    API ࢓༷Λ҆શ & ༰қʹΞοϓσʔτ͍ͯ͘͜͠ͱ͕Ͱ͖Δ
    Conclusion
    JSON
    Schema RAML
    include
    API Document
    URL Λ JS ͷ
    ม਺ͱͯ͠ఆٛ
    ValidaFlowType
    Stub Object

    View Slide

  54. 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)
    ‣ ݫີ͕͞ॊೈ͞ΛੜΈग़͢

    View Slide