åçã«åæå®ãããèšèªã®äž»ãªåé¡ã®1ã€ã¯ãããšãã°ããã©ã¡ãŒã¿ãŒãŸãã¯å€æ°ãnull以å€ã®å€ã«åŒ·å¶çã«èšå®ã§ããªããããããŒã¿ãããŒãæ£ããããšãåžžã«ä¿èšŒã§ãããšã¯éããªãããšã§ãããã®ãããªå ŽåãåçŽãªã³ãŒãã䜿çšããåŸåããããŸãã
function foo (mustExist) {
if (!mustExist) throw new Error('Parameter cannot be null')
return ...
}
ãã®ã¢ãããŒãã®åé¡ã¯ã³ãŒãã®æ±æã§ããã©ãã§ãå€æ°ããã¹ãããå¿ èŠããããç¹ã«å€æ°ãŸãã¯ãã©ã¡ãŒã¿ãŒãnullã«ã§ããªãç¶æ³ã§ã¯ããã¹ãŠã®éçºè ãå®éã«ãã®ãã¹ããåžžã«å®è¡ããããšãä¿èšŒããæ¹æ³ããªãããã§ããå€ãã®å Žåããã®ãããªãã©ã¡ãŒã¿ã®å€ãæªå®çŸ©ãŸãã¯nullã«ãªãå¯èœæ§ãããããšããããããŸãããããã¯ãããŸããŸãªã¹ãã·ã£ãªã¹ããã¯ã©ã€ã¢ã³ããšãµãŒããŒã®éšåã§äœæ¥ããå Žåãã€ãŸãã»ãšãã©ã®å Žåã«çºçããŸãã
ãã®ã·ããªãªãå°ãæé©åããããã«ãç§ã¯é©ãã®èŠå ãæå°éã«æããããã®æè¯ã®æ¹æ³ãšæ¹æ³ãæ¢ãå§ããŸããããã®æããšãªãã¯ã»ãšãªãªããã®çŽ æŽãããèšäºã«åºããããŸããã..ããã®äœåã®ç®çã¯ã圌ã®èšäºã«å®å šã«åè«ããããšã§ã¯ãªããJavaScriptéçºã®åéã§ã®çµéšã®ãããã§ç§ãæéããããŠçºèŠããããšãã§ããèå³æ·±ãæ å ±ãè¿œå ããããšã§ãã
å§ããåã«ããã®èšäºã§åãäžããŠããããã€ãã®ãã€ã³ãã確èªãããµãŒããŒã³ã³ããŒãã³ãéçºè ãšããŠã®ç§ã®æèŠãè¿°ã¹ãããšæããŸããå¥ã®èšäºã¯ããã¯ã©ã€ã¢ã³ãæåã§ããããã§ãã
ãã¹ãŠãå§ãŸã£ãçµç·¯
ããŒã¿åŠçã®åé¡ã¯ãããã€ãã®èŠå ãåå ã§ããå¯èœæ§ããããŸãããã¡ãããäž»ãªçç±ã¯ãŠãŒã¶ãŒå ¥åã§ãããã ããå¥ã®èšäºã§èšåãããŠãããã®ã«å ããŠãäžæ£ãªåœ¢åŒã®ããŒã¿ã®ä»ã®ãœãŒã¹ããããŸãã
- ããŒã¿ããŒã¹ã¬ã³ãŒã
- nullããŒã¿ãæé»çã«è¿ãé¢æ°
- å€éšAPI
æ€èšãããã¹ãŠã®ã±ãŒã¹ã§ãããŸããŸãªè§£æ±ºçãé©çšãããŸããåŸã§ãã©ããäžèœè¬ã§ã¯ãªãããšãæãåºããªãããããããã詳现ã«åæããŸããåé¡ã®ã»ãšãã©ã¯äººçºçãšã©ãŒã«ãã£ãŠåŒãèµ·ããããŸãïŒå€ãã®å Žåãèšèªã¯nullãŸãã¯æªå®çŸ©ã®ããŒã¿ïŒnullãŸãã¯æªå®çŸ©ïŒã§åäœããããã«æºåãããŠããŸããããã®ããŒã¿ãå€æããéçšã§ããããåŠçããæ©èœã倱ãããå¯èœæ§ããããŸã
ãŠãŒã¶ãŒãå ¥åããããŒã¿
ãã®å Žåãæ©äŒã¯ã»ãšãã©ãããŸãããåé¡ããŠãŒã¶ãŒå ¥åã§ããå Žåã¯ããããããã€ãã¬ãŒã·ã§ã³ã§è§£æ±ºã§ããŸãïŒã€ãŸãããŠãŒã¶ãŒãïŒããšãã°ãAPIãã€ããŒãã®äžéšãšããŠïŒéä¿¡ããçã®å ¥åãååŸããŠãããã䜿çšãããã®ã«å€æããå¿ èŠããããŸãããšã©ãŒãªãã§äœæ¥ã§ããŸãïŒã
ãµãŒããŒåŽã§ã¯ãExpressãªã©ã®WebãµãŒããŒã䜿çšããå ŽåãJSONã¹ããŒã ãJoiãªã©ã®æšæºããŒã«ã䜿çšããŠãã¯ã©ã€ã¢ã³ãåŽã§ãŠãŒã¶ãŒå ¥åã䜿çšããŠãã¹ãŠã®æäœãå®è¡ã§ããŸãã
ExpressãŸãã¯AJVã䜿çšããŠå®è¡ã§ããããšã®äŸã以äžã«ç€ºããŸãã
const Ajv = require('ajv')
const Express = require('express')
const bodyParser = require('body-parser')
const app = Express()
const ajv = new Ajv()
app.use(bodyParser.json())
app.get('/foo', (req, res) => {
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
password: { type: 'string' },
email: { type: 'string', format: 'email' }
},
additionalProperties: false
required: ['name', 'password', 'email']
}
const valid = ajv.validate(schema, req.body)
if (!valid) return res.status(422).json(ajv.errors)
// ...
})
app.listen(3000)
èŠãŠãã ããïŒç§ãã¡ã¯ã«ãŒãã®äž»èŠéšåããã§ãã¯ããŠããŸããããã©ã«ãã§ã¯ãããã¯ãã€ããŒãã®äžéšãšããŠbody-parserããã±ãŒãžããååŸãããªããžã§ã¯ãã§ãããã®å ŽåãJSONã¹ããŒããä»ããŠæž¡ãããããããã®ããããã£ã®1ã€ãç°ãªãã¿ã€ããŸãã¯åœ¢åŒïŒé»åã¡ãŒã«ã®å ŽåïŒã§ãããã©ãããæ€èšŒãããŸãã
éèŠïŒæªåŠçã®ãªããžã§ã¯ãã«å¯ŸããŠHTTP422ãè¿ãããšã«æ³šæããŠãã ãããå€ãã®äººã¯ãç¡å¹ãªæ¬æãã¯ãšãªæååãªã©ã®ã¯ãšãªãšã©ãŒããšã©ãŒ400ç¡å¹ãªã¯ãšãªãšããŠè§£éã ãŸãã -ããã¯éšåçã«ã¯çå®ã§ããããã®å Žåãåé¡ã¯ãªã¯ãšã¹ãèªäœã§ã¯ãªãããŠãŒã¶ãŒããªã¯ãšã¹ããšãšãã«éä¿¡ããããŒã¿ã«ãããŸããããããã£ãŠããŠãŒã¶ãŒãžã®æé©ãªå¿çã¯ãšã©ãŒ422ã«ãªããŸããããã¯ãèŠæ±ãæ£ããããšãæå³ããŸããããã®å 容ãäºæããã圢åŒã§ã¯ãªããããåŠçã§ããŸããã
å¥ã®ãªãã·ã§ã³ïŒAJVã®äœ¿çšä»¥å€ïŒã¯ãRozã§äœæããã©ã€ãã©ãªã䜿çšããããšã§ãããããExpressoãšåŒã³ãŸããããã¯ãExpressã䜿çšããAPIã®éçºãå°ãç°¡åã«ããäžé£ã®ã©ã€ãã©ãªã§ãããã®ãããªããŒã«ã®1ã€ã¯ @ expresso / validatorã§ããããã¯åºæ¬çã«äžèšã§ç€ºãããã®ãå®è¡ããŸãããããã«ãŠã§ã¢ãšããŠåŒãæž¡ãããšãã§ããŸãã
ããã©ã«ãå€ã®è¿œå ãã©ã¡ãŒã¿ãŒ
以åã«ç¢ºèªããããšã«å ããŠããªãã·ã§ã³ã®ãã£ãŒã«ãã§éä¿¡ãããªãå Žåã«ãã¢ããªã±ãŒã·ã§ã³ã«nullå€ãæž¡ãå¯èœæ§ãããããšãçºèŠããŸãããããšãã°ãããŒãžãšãµã€ãºã®2ã€ã®ãã©ã¡ãŒã¿ãã¯ãšãªæååãšããŠåãåãããŒãžããŒã·ã§ã³ã«ãŒãããããšããŸãããã ãããããã¯ãªãã·ã§ã³ã§ãããåä¿¡ãããªãå Žåã¯ããã©ã«ãã§ããã©ã«ãã«èšå®ããå¿ èŠããããŸãã
çæ³çã«ã¯ãã³ã³ãããŒã©ãŒã«ã¯æ¬¡ã®ãããªæ©èœãå¿ èŠã§ãã
function searchSomething (filter, page = 1, size = 10) {
// ...
}
泚æãããŒãžã³ã°èŠæ±ã«å¿çããŠè¿ããã422ãšã©ãŒãšåæ§ã«ãæ£ãããšã©ãŒã³ãŒã206äžå®å šãªã³ã³ãã³ããè¿ãããšãéèŠã§ããè¿ãããããŒã¿ã®éãå šäœã®äžéšã§ããèŠæ±ã«å¿çãããšãã¯ãã€ã§ãã 206ãŠãŒã¶ãŒãæåŸã®ããŒãžã«éãããã以äžã®ããŒã¿ãååšããªãå Žåãæã ã¯200ã®ã³ãŒããè¿ãããšãã§ãããŠãŒã¶ãŒãç·ããŒãžç¯å²å€ã®ããŒãžãèŠã€ããããšãããšããæã ã¯ã³ãŒã204ãè¿ããããŸããã³ã³ãã³ããã
ããã«ããã2ã€ã®ç©ºã®å€ãååŸãããšãã«åé¡ã解決ãããŸãããããã¯äžè¬ã«JavaScriptã®éåžžã«ç©è°ãéžãåŽé¢ã§ãããªãã·ã§ã³ã®ãã©ã¡ãŒã¿ã¯ãå€ã空ã®å Žåã«ã®ã¿ããã©ã«ãå€ãåããŸããããã®ã«ãŒã«ã¯å€nullã«å¯ŸããŠã¯æ©èœããªãããã次ã®ããã«ãããšæ¬¡ã®ããã«ãªããŸãã
function foo (a = 10) {
console.log(a)
}
foo(undefined) // 10
foo(20) // 20
foo(null) // null
æ å ±ãnullãšããŠæ±ãå¿ èŠãããããããªãã·ã§ã³ã®ãã©ã¡ãŒã¿ãŒã ãã«äŸåããããšã¯ã§ããŸããããããã£ãŠããã®ãããªå Žåã次ã®2ã€ã®æ¹æ³ããããŸã
ã1ãã³ã³ãããŒã©ãŒã§Ifã¹ããŒãã¡ã³ãã䜿çšãã
function searchSomething (filter, page = 1, size = 10) {
if (!page) page = 1
if (!size) size = 10
// ...
}
èŠãç®ã¯ããŸãè¯ããªããããªãäžäŸ¿ã§ãã
2. ã«ãŒãäžã§çŽæ¥JSONã¹ããŒãã䜿çšãã
ããã§ããAJVãŸãã¯@ expresso / validatorã䜿çšããŠãã®ããŒã¿ãæ€èšŒã§ããŸãã
app.get('/foo', (req, res) => {
const schema = {
type: 'object',
properties: {
page: { type: 'number', default: 1 },
size: { type: 'number', default: 10 },
},
additionalProperties: false
}
<a href=""></a> const valid = ajv.validate(schema, req.params)
if (!valid) return res.status(422).json(ajv.errors)
// ...
})
ãã«å€ãšæªå®çŸ©å€ã®æäœ
ããã€ãã®çç±ãããJavaScriptã§nullãšundefinedã®äž¡æ¹ã䜿çšããŠå€ã空ã§ããããšã蚌æãããšããã¢ã€ãã¢ã«ã¯å人çã«æºè¶³ããŠããŸããããããã®æŠå¿µãæœè±¡çãªã¬ãã«ã«ããããšã®é£ããã«å ããŠããªãã·ã§ã³ã®ãã©ã¡ãŒã¿ãŒãå¿ããŠã¯ãªããŸããããããã®æŠå¿µã«ã€ããŠãŸã çåãããå Žåã¯ãå®è·µããã®çŽ æŽãããäŸãæããŸãããã
å®çŸ©ãç解ããã®ã§ã2020幎ã«ã¯JavaScriptã«2ã€ã®äž»èŠãªé¢æ°ããããŸããnullåäœæŒç®åãšãªãã·ã§ã³ã®ãã§ãŒã³ãããã«ã€ããŠã¯ãã§ã«èšäºãæžããŠããã®ã§ãããã§ã¯è©³ãã説æããŸãã ã ïŒãã«ãã¬ã«èªã§ãïŒãã ããïŒobjãªã©ã®è«ççãªåŠå®ã䜿çšãã代ããã«ãé©åãªæŒç®åïŒ??ïŒã䜿çšããŠãnullãšundefinedã®2ã€ã®æŠå¿µã«éäžã§ãããããããã2ã€ã®ã€ãããŒã·ã§ã³ã«ãã£ãŠã¿ã¹ã¯ãå€§å¹ ã«ç°¡çŽ åãããããšã«æ³šæããŠãã ãããããã¯ééãã®è¥æ²ãªåå°ã§ãã
æé»çã«nullãè¿ãé¢æ°
ãã®åé¡ã¯ããã®æé»ã®æ§è³ªã®ããã«è§£æ±ºããã®ãã¯ããã«å°é£ã§ããäžéšã®é¢æ°ã¯ãåžžã«æäŸãããããšãåæãšããŠããŒã¿ãåŠçããŸãããããã§ãªãå ŽåããããŸããæšæºçãªäŸãèããŠã¿ãŸãããïŒ
function foo (num) {
return 23*num
}
numãnullã®å Žåããã®é¢æ°ã®çµæã¯0ã«ãªããŸãããããã¯äºæãããŠããŸããã§ããããã®ãããªå Žåãã³ãŒãããã¹ããã以å€ã«éžæè¢ã¯ãããŸãããå®è¡ã§ãããã¹ãã«ã¯2ã€ã®ã¿ã€ãããããŸãã1ã€ç®ã¯ãåçŽãªifã¹ããŒãã¡ã³ãã䜿çšããããšã§ãã
function foo (num) {
if (!num) throw new Error('Error')
return 23*num
}
2çªç®ã®æ¹æ³ã¯ãã©ã¡ããã®ã¢ããã䜿çšããããšã§ããããã«ã€ããŠã¯ãåè¿°ã®èšäºã§è©³ãã説æããŠããŸããããã¯ããããŸããªããŒã¿ãã€ãŸãnullã®å Žåãšnullã§ãªãå Žåã®ããŒã¿ãåŠçããããã®åªããæ¹æ³ã§ããããã¯ãJavaScriptã«ã¯ã2ã€ã®ã¢ã¯ã·ã§ã³ã¹ããªãŒã ããµããŒãããçµã¿èŸŒã¿é¢æ°ããã§ã«å«ãŸããŠããããã§ã-PromiseïŒ
function exists (value) {
return x != null ? Promise.resolve(value) : Promise.reject(`Invalid value: ${value}`)
}
async function foo (num) {
return exists(num).then(v => 23 * v)
}
ããã¯ãcatchã¹ããŒãã¡ã³ããexistsããfooãåŒã³åºããé¢æ°ã«å§ä»»ããæ¹æ³ã§ãã
function init (n) {
foo(n)
.then(console.log)
.catch(console.error)
}
init(12) // 276
init(null) // Invalid value: null
å€éšAPIãšããŒã¿ããŒã¹ã¬ã³ãŒã
ããã¯éåžžã«äžè¬çãªã±ãŒã¹ã§ããç¹ã«ã以åã«äœæãŸãã¯å ¥åãããããŒã¿ããŒã¹ããéçºãããã·ã¹ãã ãããå Žåã¯ããã§ããããšãã°ãæåããåä»»è ãšåãããŒã¿ããŒã¹ã䜿çšããŠãç°ãªãã·ã¹ãã ã®ãŠãŒã¶ãŒãçµ±åããæ°è£œåãªã©ã
ããã«é¢ãã倧ããªåé¡ã¯ãããŒã¿ããŒã¹ãäžæã§ãããšããäºå®ã§ã¯ãããŸãããå®éããããçç±ã§ããããŒã¿ããŒã¹ã¬ãã«ã§äœãè¡ããããããããããå€ãnullãŸãã¯æªå®çŸ©ã®ããŒã¿ãåä¿¡ãããã©ããã確èªã§ããªãããã§ãã ...ããŒã¿ããŒã¹ãé©åã«ææžåãããŠãããã以åãšåãåé¡ã«çŽé¢ããŠããå Žåãäœå質ã®ææžåã«ã€ããŠèšããããåŸãŸããã
ããã§ã§ããããšã¯äºå®äžäœããããŸãããå人çã«ã¯ãããŒã¿ã®ç¶æ ããã§ãã¯ããŠãããŒã¿ãåŠçã§ããããšã確èªããããšã奜ã¿ãŸãããã ããè¿ããããªããžã§ã¯ãã®å€ãã¯åã«å€§ããããå¯èœæ§ãããããããã¹ãŠã®ããŒã¿ãæ€èšŒããããšã¯ã§ããŸããããããã£ãŠãæäœãå®è¡ããåã«ããããããã£ã«ã¿ãŒãªã©ãé¢æ°ã®æäœã«é¢é£ããããŒã¿ããã§ãã¯ããŠãå®çŸ©ãããŠããªããã©ããã確èªããããšããå§ãããŸãã
ãšã©ãŒã®çæ
ããŒã¿ããŒã¹ãšå€éšAPIã«ã¢ãµãŒã·ã§ã³é¢æ° ã䜿çšããããšããå§ãããŸããåºæ¬çã«ããããã®é¢æ°ã¯ããŒã¿ãããå Žåã¯ãããè¿ããããã§ãªãå Žåã¯ãšã©ãŒãçæãããŸãããã®ã¿ã€ãã®é¢æ°ã®æãäžè¬çãªäœ¿çšäŸã¯ãAPIãããå Žåã§ããããšãã°ãèå¥åã§ç¹å®ã®ããŒã¿ã¿ã€ããæ€çŽ¢ããå Žåãããç¥ãããŠããfindByIdã§ãã
async function findById (id) {
if (!id) throw new InvalidIDError(id)
const result = await entityRepository.findById(id)
if (!result) throw new EntityNotFoundError(id)
return result
}
EntityãUserNotFoundErrorãªã©ã®ãšã³ãã£ãã£ã®ååã«çœ®ãæããŸãã
ããã¯ãåãã³ã³ãããŒã©ãŒå ã«IDã§ãŠãŒã¶ãŒãæ€çŽ¢ããé¢æ°ãšããã®ãŠãŒã¶ãŒã䜿çšããŠä»ã®ããŒã¿ïŒããšãã°ãããŒã¿ããŒã¹ã®å¥ã®ã³ã¬ã¯ã·ã§ã³å ã®ãã®ãŠãŒã¶ãŒã®ãããã¡ã€ã«ïŒãæ€çŽ¢ããå¥ã®é¢æ°ãå«ããããšãã§ãããããåªããŠããŸãããããã¡ã€ã«ã«ãã¯ã¢ããé¢æ°ãåŒã³åºããšãã¯ãã¢ãµãŒã·ã§ã³ã䜿çšããŠããŠãŒã¶ãŒãå®éã«ããŒã¿ããŒã¹ã«ååšããããšã確èªããŸãããã以å€ã®å Žåãé¢æ°ã¯å®è¡ããããã«ãŒãäžã§çŽæ¥ãšã©ãŒãæ€çŽ¢ã§ããŸãã
async function findUser (id) {
if (!id) throw new InvalidIDError(id)
const result = await userRepository.findById(id)
if (!result) throw new UserNotFoundError(id)
return result
}
async function findUserProfiles (userId) {
const user = await findUser(userId)
const profile = await profileRepository.findById(user.profileId)
if (!profile) throw new ProfileNotFoundError(user.profileId)
return profile
}
æåã®é¢æ°ã¯ãŠãŒã¶ãŒãååšããããšã確èªããããããŠãŒã¶ãŒãååšããªãå Žåã¯ããŒã¿ããŒã¹åŒã³åºããè¡ããªãããšã«æ³šæããŠãã ãããããã§ãã«ãŒãã§æ¬¡ã®ãããªããšãã§ããŸãã
app.get('/users/{id}/profiles', handler)
// --- //
async function handler (req, res) {
try {
const userId = req.params.id
const profile = await userService.getProfile(userId)
return res.status(200).json(profile)
} catch (e) {
if (e instanceof UserNotFoundError || e instanceof ProfileNotFoundError) return res.status(404).json(e.message)
if (e instanceof InvalidIDError) return res.status(400).json(e.message)
}
}
æ¢åã®ãšã©ãŒã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹åã確èªããã ãã§ãè¿ããããšã©ãŒã®çš®é¡ã確èªã§ããŸãã
çµè«
ç¶ç¶çã§äºæž¬å¯èœãªæ å ±ã®æµãã確ä¿ããããã«ããŒã¿ãåŠçããæ¹æ³ã¯ããã€ããããŸããä»ã«äœãã¢ããã€ã¹ã¯ãããŸããïŒïŒã³ã¡ã³ãæ¬ã«æ®ããŠãã ãã
çŽ æã®ããã«ïŒïŒã¢ããã€ã¹ãããããæèŠãè¿°ã¹ããããããã¯ãã æšæ¶ãããã§ããïŒãœãŒã·ã£ã«ã¡ãã£ã¢ã§ç§ãèŠã€ããæ¹æ³ã¯æ¬¡ã®ãšããã§ãã
ãã®èšäºã¯å ã ãLucasSantosã«ãã£ãŠdev.toã«æçš¿ãããŸãããèšäºã®ãããã¯ã«ã€ããŠè³ªåãã³ã¡ã³ããããå Žåã¯ãdev.toã®å ã®èšäºã®äžã«æçš¿ããŠãã ããã