Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

graphql + mongoose + typescript, how to reduce model definition duplicated

I use mongoose, typescript and graphql to build my app.

I am a full-stack developer.

The problem is I define the fields and types of model FIVE times.

server side:

models/book.ts:

// first time
interface IBook extends mongoose.Document {
  title: string;
  author: string;
} 

// second time
const bookSchema = new mongoose.Schema({
  title: String,
  author: String
})

const Book: mongoose.Model<IBook> = mongoose.model<IBook>('Book', bookSchema)

graphql/typeDefs.ts

const typeDefs = `
  // third time
  type Book {
    title: String!
    author: String!
  }

  // fourth time
  input BookInput {
    title: String!
    author: String!
  }
`

client side:

interfaces/book.ts

// fifth time
interface IBook {
  title: string;
  author: string;
}

As you can see. the title and author fields and types are defined FIVE times.

There are three main disadvantages:

  1. duplicated
  2. lack of maintainability
  3. inefficient

Is there a way to solve this? I think this is almost a DRY problem.

Here are my thinkings:

  1. universal app - extract some common modules used in client and server side.
  2. make a tool handle this.

    • make a project generator or command line tool like ng-cli for generating model and types statically which means before the run-time

    • make model definition decorator or syntax sugar generate model and types dynamically at run-time

like image 904
slideshowp2 Avatar asked Aug 08 '18 06:08

slideshowp2


Video Answer


1 Answers

We recently ran into this issue, requiring us to maintaining a duplicated Typescript interface alongside our Mongoose schemas (for us the issue was solely server-side as we are not using graphql or client-side Typescript)

We built mongoose-tsgen to address this. It may not handle all cases concerned here, but could be easily bootstrapped to handle your use case.

It reads your Mongoose schemas and generates an index.d.ts file which includes an interface for each document and subdocument. These interfaces can be imported from the mongoose module directly like so:

import { IUser } from "mongoose"

async function getUser(uid: string): IUser {
  // `user` is of type IUser
  const user = await User.findById(uid)
  return user;
}

like image 192
Francesco Virga Avatar answered Sep 30 '22 15:09

Francesco Virga