I am building an API where the client can send a url
and some comma-delimited tags
:
{
"url": "https://www.youtube.com/watch?v=pquxHIBx8ks",
"tags": "JavaScript, Sequelize"
}
Before processing this data, I want to validate the input. To do that, I wrote a middleware function called validateSubmitScreencastReq
:
import config from 'config'
import youtubeUrl from 'youtube-url'
import db from 'sequelize-connect'
import * as youtubeClient from '../../source/util/youtubeClient'
async function validateUrl (url) {
if (!url) {
return 'url cannot be undefined'
}
if (!youtubeUrl.valid(url)) {
return 'url must be a valid YouTube URL'
}
const client = youtubeClient.create(config.youtubeApiKey)
if (!await client.videoExists(url)) {
return 'url must link to an existent, public YouTube video'
}
const foundScreencast = await db.models.screencast.findOne({
where: {
url: url
}
})
if (foundScreencast !== null) {
return 'url has already been submitted'
}
return undefined
}
function validateTags (tags) {
if (!tags) {
return 'tags cannot be undefined'
}
if (typeof tags !== 'string') {
return 'tags must be a string'
}
return undefined
}
export async function validateSubmitScreencastReq (req, res, next) {
try {
const urlError = await validateUrl(req.body.url)
const tagError = validateTags(req.body.tags)
const errors = []
if (urlError) {
errors.push({
field: 'url',
message: urlError
})
}
if (tagError) {
errors.push({
field: 'tags',
message: tagError
})
}
if (errors.length !== 0) {
res.status(400).json({errors})
} else {
next()
}
} catch (error) {
next(error)
}
}
I'm not too happy with this code.
The function essentially aggregates error messages, which makes me think of using reduce
but I can't see how to make it work elegantly.
What's more, I don't think I'm expressing that the function aggregates errors very clearly.
How can I improve the readability the above code, preferably using functional concepts?
(If it's at all useful, here is the validateSubmitScreencastReq
function on GitHub and here are the corresponding unit tests.)
if (req.body.hasOwnProperty('url')) { // object has 'url' prop ...
... If it has the property you don't have to worry about the function getting undefined variable passed. ...if (typeof tags !== 'string') {
will pass with an empty string (''
), why not check fortags.length
... I'm not sure about the framework but thetry/catch
doesn't have anythrow
s in there. I think it's probably better to just send the first error encountered back and break, but that could depend on what you're trying to do. \$\endgroup\$