JavaScript: Ajv (json-schema)
I'd say for now the accepted standard definitely seems to be Ajv.
It has 78M weekly downloads at the time of writing (2024).
It uses json-schema
(a widely accepted standard for API constracts, testing and validations) to validate objects.
https://github.com/ajv-validator/ajv
TypeScript: typescript-json-schema
You could use typescript-json-schema (270k weekly downloads) to automatically generate json-schemas from your types.
Ajv (see above) will be able to use those json-schemas to validate in runtime.
Example
Given the following types
import type { FieldValue } from '@firebase/firestore'
export type Priority = 'low' | 'medium' | 'high'
export type Status = 'open' | 'closed'
export interface TicketData {
id: string
userId: string
userEmail: string
title: string
body: string
priority: Priority
status: Status
createdAt: FieldValue
updatedAt: FieldValue
}
export type CreateTicketData = Pick<TicketData, 'title' | 'body' | 'priority'>
You can use typescript-json-schema
to create a JSON file for your type like so (explained here):
typescript-json-schema src/data/schema/TicketData.ts CreateTicketData > src/data/schema/CreateTicketData.json
The result would be CreateTicketData.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"Priority": {
"enum": [
"high",
"low",
"medium"
],
"type": "string"
}
},
"properties": {
"body": {
"type": "string"
},
"priority": {
"$ref": "#/definitions/Priority"
},
"title": {
"type": "string"
}
},
"type": "object"
}
You can now use that json
file in your backend code like so:
import Ajv from 'ajv'
import schema from '@/data/schema/CreateTicketData.json'
export async function POST(request: Request) {
try {
// Verify request
const { token, ticketData } = await request.json()
await admin.auth().verifyIdToken(token)
// Validate data
const validate = new Ajv({ allErrors: true }).compile(schema)
if (!validate(ticketData)) return Response.json({ error: validate.errors }, { status: 400 })
// Create ticket and send 201 'Created' response.
const persistedTicket = someLogic(ticketData)
return Response.json(persistedTicket, { status: 201 })
} catch (error) {
return Response.json(
{ error: 'Unauthorized', reason: isDev ? error : 'Something went wrong' },
{ status: 401 },
)
}
}
Alternatives
revalidator
to validate your data – Overpoweringrevalidator
, as it has it's own schema structure which is completely different. (unless you were to write a plugin to convert the typescript interfaces to revalidator schema) – Overpowering