M. Mirra's notebook[?]

created: 2019/05/27, tags: node

validate incoming API data and get editor completion and type safety for free

assume an API to save bookmarks:

// bookmarkapi/index.ts

import express, { json } from 'express'
import * as foreign from './foreign'

const app = express()

app.post('/items', json(), async (req, res) => {
  const itemSubmission = foreign.itemSubmission(req.body)
  // itemSubmission will have a string 'url' field
  // and an optional 'title' field

benefits of the code above:

  • the point where foreign data can be considered valid is clearly marked
  • invalid data immediately causes a validation error [1]
  • itemSubmission is fully typed, providing both safety and convenience (intellisense)
  • data is validated through more granular checks than those allowed typescript's: not just "string" but "email" or "url"; not just "number" but "integer between 10 and 1000"
  • no typescript definition needs to be written and kept in sync with the foreign data definition
  • foreign data is described in one place only

how it works

the heavy lifting is courtesy of typesafe-joi [2]:

// bookmarkapi/foreign.ts

import joi from 'typesafe-joi'

const schemas = {
  itemSubmission: joi.object().keys({
    url: joi.string().uri({
      scheme: ['http', 'https']
    title: joi.string().allow('').optional(),

export const itemSubmission = (data: any) =>
  joi.attempt(data, schemas.itemSubmission)

and if for any reason an explicit type for itemSubmission is needed:

// bookmarkapi/foreign.ts
// ...

export type ItemSubmission = ReturnType<typeof itemSubmission>