4

My objective is to improve data quality in our MongoDB db - by using JSON Schema validation. We are using typescript in our project, and have interfaces for all our collections.

So I'm basically looking for an effective way of;

Converting this interface:

import { ObjectId } from 'mongodb';

export interface Category {
  _id: ObjectId;
  date: Date;
  level: string | null;
}

Into this JSON Schema

export const CategoryJSONSchema = {
  required: ['_id', 'date', 'level'],
  additionalProperties: false,
  properties: {
    _id: { bsonType: 'objectId' },
    date: { bsonType: 'date' },
    level: { oneOf: [{ bsonType: 'null' }, { bsonType: 'string' }] }
  }
}
DauleDK
  • 3,313
  • 11
  • 55
  • 98
  • A script or at runtime? – Newbie Oct 01 '21 at 07:08
  • Script would be fine - no need for runtime :) – DauleDK Oct 01 '21 at 07:12
  • Are we talking full support of JSON Schema validation? Including properties of type object and array? Also handle typescript optional properties eg optionalValue?: string – Mikael Hellman Oct 01 '21 at 10:45
  • @MikaelHellman I would like full support of optional properties, object and array - we use that extensively. However, if a given implementation is able to generate 90% of the interfaces, then I'm happy to write the remaining 10% manually ☺️ – DauleDK Oct 01 '21 at 11:20
  • Instead of using interfaces or mongojson have you considered using a orm? It'll allow you to write your schemas in one place which you can then use as interfaces and it'll manage the db types – Schalton Oct 01 '21 at 18:26
  • @Schalton I prefer the native driver, i MO it's more flexible when using "advanced" features like update aggregation pipeline, query pipelines, projections, change streams etc. – DauleDK Oct 03 '21 at 18:09
  • [Hygen](http://www.hygen.io) would be really nice way to get this done, though you would have to run a command to consume the interfaces and a somewhat rigid structure for all of them. – bloo Oct 05 '21 at 08:28
  • 1
    [this](https://github.com/YousefED/typescript-json-schema) might be useful. But it requires comments and extra work may be needed for annotating types (in default it generates `type : 'date'` like schema). – Eldar Oct 05 '21 at 20:09
  • I second @Eldar, typescript-json-schema is the way to go. I would also check https://github.com/vega/ts-json-schema-generator. It's a similar tool but uses own AST and might be a bit more flexible with custom parsing and formatting to handle bson types. – Alex Blex Oct 05 '21 at 22:29
  • @AlexBlex - The provided libraries were part of my original research - so I guess the question really boils down to the question about how to generate the BSON types – DauleDK Oct 06 '21 at 06:32
  • You want an ORM / ODM for it which is something entirely on the client end and you can manage it using any external module or custom code if the driver doesn't allow it. For Java for example, the default driver can handle these things. Also you can add these validations on the MongoDB Server side so it won't accept if it doesn't follow the validation you want it to follow. Similar to how SQL databases work for example. EDIT : You already know the server side validation. – Gandalf the White Oct 07 '21 at 08:48
  • @GandalftheWhite what I wan't is described in the question – DauleDK Oct 07 '21 at 08:57
  • @DauleDK It's actually confusing :D You know what the scenario is, what are you precisely looking for, the default node.js for example doesn't have it. You know a custom solution is needed. – Gandalf the White Oct 07 '21 at 09:07

2 Answers2

1

You need a custom ts-transformer, to generate json schema.

Here's a ts-transformer-keys example

import { keys } from 'ts-transformer-keys';

interface Props {
  id: string;
  name: string;
  age: number;
}
const keysOfProps = keys<Props>();

console.log(keysOfProps); // ['id', 'name', 'age']

I would fork the package, tweak it so that it also exposes types of field. Then having type information, it would be easy to generate json or model. Similar to what Prisma.io does.

Also there is ts-transformer-enumerate

Medet Tleukabiluly
  • 11,662
  • 3
  • 34
  • 69
-2

Have you considered using mongoose? You can force your schema to comply with an interface. For example:

const CategorySchema = new mongoose.Schema<CategoryInterface>({ ... });
Giusseppe
  • 147
  • 7
  • 3
    Yes I have considered `mongoose`, but that's not part of the solution space for the question. – DauleDK Oct 04 '21 at 17:21