{"id":20463477,"url":"https://github.com/lstkz/ts-mongoose","last_synced_at":"2025-08-02T05:09:20.072Z","repository":{"id":46928957,"uuid":"160231488","full_name":"lstkz/ts-mongoose","owner":"lstkz","description":"Automatically infer TypeScript interfaces from mongoose schemas🙀","archived":false,"fork":false,"pushed_at":"2023-10-25T09:32:01.000Z","size":5274,"stargazers_count":212,"open_issues_count":47,"forks_count":23,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-25T08:53:46.737Z","etag":null,"topics":["converter","mongodb","mongoose","odm","ts","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lstkz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-03T17:52:02.000Z","updated_at":"2025-07-22T11:30:20.000Z","dependencies_parsed_at":"2025-02-02T05:20:31.558Z","dependency_job_id":null,"html_url":"https://github.com/lstkz/ts-mongoose","commit_stats":null,"previous_names":["bettercallsky/ts-mongoose"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lstkz/ts-mongoose","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lstkz%2Fts-mongoose","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lstkz%2Fts-mongoose/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lstkz%2Fts-mongoose/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lstkz%2Fts-mongoose/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lstkz","download_url":"https://codeload.github.com/lstkz/ts-mongoose/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lstkz%2Fts-mongoose/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268338269,"owners_count":24234540,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["converter","mongodb","mongoose","odm","ts","typescript"],"created_at":"2024-11-15T13:11:30.631Z","updated_at":"2025-08-02T05:09:20.046Z","avatar_url":"https://github.com/lstkz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DEPRACATED ts-mongoose\n\n[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)\n\n\u003e\n\u003e [!WARNING]  \n\u003e PACKAGE IS DEPRACATED AND WILL NOT BE SUPPORTED ANYMORE. PLEASE USE OFFICIAL MONGOOSE TYPESCRIPT SUPPORT.\n\u003e\n\n\nAutomatically infer TypeScript interfaces from mongoose schemas.\n\n## Installation\n\n```bash\nnpm i ts-mongoose mongoose @types/mongoose\nyarn add ts-mongoose mongoose @types/mongoose\n```\n\n## The Problem\n\nWhen using mongoose and Typescript, you must define schemas and interfaces. Both definitions must be maintained separately and must match each other. It can be error-prone during development and cause overhead.\n\n`ts-mongoose` is a very lightweight library that allows you to create a mongoose schema and a typescript type from a common definition.  \nAll types as created from 1-liner functions and does not depend on decorators❗️.\n\nFor example:  \n`Type.string({ required: true })` returns `{type: String, required: true}`, which is the same definition required in the original mongoose library.\n\n## Example\n\nBefore:\n\n```ts\nimport { Schema, model, Model, Document } from 'mongoose';\n\nconst AddressSchema = new Schema(\n  {\n    city: { type: String, required: true },\n    country: String,\n    zip: String,\n  },\n  { _id: false, timestamps: true }\n);\n\nconst PhoneSchema = new Schema({\n  phoneNumber: { type: Schema.Types.Number, required: true },\n  name: String,\n});\n\nconst UserSchema = new Schema(\n  {\n    title: { type: String, required: true },\n    author: { type: String, required: true },\n    body: { type: String, required: true },\n    comments: [\n      {\n        body: { type: String, required: true },\n        date: { type: Date, required: true },\n      },\n    ],\n    date: { type: Date, default: Date.now, required: true },\n    hidden: { type: Boolean, required: true },\n    meta: {\n      votes: { type: Schema.Types.Number },\n      favs: { type: Schema.Types.Number },\n    },\n    m: {\n      type: Schema.Types.Mixed,\n      required: true,\n    },\n    gender: {\n      type: Schema.Types.String,\n      required: true,\n      enum: ['male', 'female'],\n    },\n    otherId: {\n      type: Schema.Types.ObjectId,\n      required: true,\n    },\n    address: {\n      type: AddressSchema,\n      required: true,\n    },\n    phones: {\n      type: [PhoneSchema],\n      required: true,\n    },\n  },\n  { timestamps: { createdAt: true } }\n);\n\ninterface UserProps extends Document {\n  title: string;\n  author: string;\n  body: string;\n  // Duplicate all props from the above schema :(\n}\n\nconst User: Model\u003cUserProps\u003e = model('User', UserSchema);\n```\n\n🎉🎉🎉 After:\n\n```ts\nimport { createSchema, Type, typedModel } from 'ts-mongoose';\n\nconst genders = ['male', 'female'] as const;\n\nconst AddressSchema = createSchema(\n  {\n    city: Type.string({ required: true }),\n    country: Type.string(),\n    zip: Type.string(),\n  },\n  { _id: false, timestamps: true }\n);\n\nconst PhoneSchema = createSchema({\n  phoneNumber: Type.number({ required: true }),\n  name: Type.string(),\n});\n\nconst UserSchema = createSchema(\n  {\n    title: Type.string({ required: true }),\n    author: Type.string({ required: true }),\n    body: Type.string({ required: true }),\n    comments: Type.array().of({\n      body: Type.string({ required: true }),\n      date: Type.date({ required: true }),\n    }),\n    date: Type.date({ default: Date.now as any }),\n    hidden: Type.boolean({ required: true }),\n    meta: Type.object().of({\n      votes: Type.number({ required: true }),\n      favs: Type.number({ required: true }),\n    }),\n    m: Type.mixed({ required: true }),\n    gender: Type.string({ required: true, enum: genders }),\n    otherId: Type.objectId({ required: true }),\n    address: Type.schema({ required: true }).of(AddressSchema),\n    phones: Type.array({ required: true }).of(PhoneSchema),\n  },\n  { timestamps: { createdAt: true } }\n);\n\nconst User = typedModel('User', UserSchema);\nUser.findById('123').then(user =\u003e {\n  if (user) {\n    user. // autocomplete here\n  }\n});\n```\n\n### API\n\n- Each type has two forms: required and optional\n\n```ts\n{\n  // same as {type: String}\n  firstName: Type.string(),\n  // same as {type: String, required: true}\n  email: Type.string({ required: true }),\n}\n```\n\n- Each type accepts the same options from mongoose\n\n```ts\n{\n  // same as {type: String, required: true, unique: true, index: true}\n  email: Type.string({ required: true, unique: true, index: true });\n}\n```\n\n- Note that enum values need to be readonly array to be treated as literals by typescript\n\n```ts\nconst genders = ['male', 'female'] as const;\n{\n  // same as {type: String, enum: ['male', 'female']}\n  gender: Type.string({ enum: genders });\n}\n```\n\n- `schema`, `object`, `array` types have a method `of` where you must provide a child type\n\n```ts\n{\n  // same as {type: [String], required: true}\n  tags: Type.array({ required: true }).of(Type.string({ required: true }));\n}\n```\n\n- `schema.of(ExampleSchema)` has typical for Subdocument additional fields and methods. Setting `{ _id: false }` in SchemaOptions won't attach `_id` property in Subdocument\n\n```ts\nconst AddressSchema = createSchema(\n  { city: Type.string({ required: true }) },\n  { _id: false, timestamps: true }\n);\n{\n  // same as {type: AddressSchema}\n  address: Type.schema().of(AddressSchema);\n}\n// address property has city property, other Subdocument methods and properties except '_id'\n```\n\n- `array.of(ExampleSchema)` will return DocumentArray instead of standard array\n\n```ts\nconst PhoneSchema = createSchema(\n  { phoneNumber: Type.number({ required: true }) },\n  { _id: false }\n);\n{\n  // same as {type: [PhoneSchema]}\n  phones: Type.array().of(PhoneSchema);\n}\n// phones property has such methods as create(), id(), but also those typical for arrays like map(), filter() etc\n```\n\n- `ref` is a special type for creating references\n\n```ts\n{\n  // same as [{type: Schema.Types.ObjectId, ref: 'Comment'}]\n  comments: Type.array().of(\n    Type.ref(Type.objectId()).to('Comment', CommentSchema)\n  ),\n}\n```\n\n- `populateTs(property: string)` use this function to populate a property and adjust the returned type automatically. Under the hood it calls only the native `populate` method.  \n  Method will be available if you import a special plugin.\n\n```ts\n// models.ts\n\nimport 'ts-mongoose/plugin';\n\nUser.find().populateTs('comments');\n```\n\n## Extracting Document type\n\nUse `ExtractDoc` to extract generated document type.  \nUse `ExtractProps` to extract generated base model properties.  \nExample:\n\n```ts\nimport {\n  createSchema,\n  Type,\n  typedModel,\n  ExtractDoc,\n  ExtractProps,\n} from 'ts-mongoose';\n\nexport const UserSchema = createSchema({\n  email: Type.string({ required: true }),\n  username: Type.string({ required: true }),\n  isBlocked: Type.boolean(),\n});\n\nexport const User = typedModel('User', UserSchema);\nexport type UserDoc = ExtractDoc\u003ctypeof UserSchema\u003e;\nexport type UserProps = ExtractProps\u003ctypeof UserSchema\u003e;\n\n// example function\n\nasync function blockUser(user: UserDoc) {\n  user.isBlocked = true;\n  // access all properties + Document methods and properties\n  await user.save();\n}\n\nfunction randomUser(): UserProps {\n  // must return `email`, `username`\n  // `isBlocked` is optional\n  return {\n    email: 'user1@example.com',\n    username: 'user1',\n  };\n}\n```\n\n## Refs\n\nRefs and populations are supported.  \nCheck code under `example/example4.ts`.\n\n![alt autocomplete](.github/refs.gif)\n\n### Custom Field\n\nIf you need to specify custom fields in the model, you can add a fake annotation.  \nIt's only required if you add virtual fields or custom methods to the model.\n\n```ts\nconst UserSchema = createSchema({\n  title: Type.string({ required: true }),\n  author: Type.string({ required: true }),\n  ...({} as {\n    generatedField: string;\n    customFunction: () =\u003e number;\n  }),\n});\nconst User = typedModel('User', UserSchema);\n```\n\nAutocomplete popup:  \n![alt autocomplete](.github/custom.png)\n\n### Static methods\n\nIf you need to have static custom methods on Model you can pass them as 5th parameter of `typedModel` function. It should automatically figured out returning value, but you can declare it too.\n\n```ts\nconst UserSchema = createSchema({\n  name: Type.string({ required: true }),\n  age: Type.number({ required: true }),\n});\n\nconst User = typedModel('User', UserSchema, undefined, undefined, {\n  findByName: function(name: string) {\n    return this.find({ name });\n  },\n  findOneByName: function(name: string) {\n    return this.findOne({ name });\n  },\n  countLetters: function(name: string, bonus?: number) {\n    return name.length + (bonus ? bonus : 0);\n  },\n});\nconst u = await User.findOne({});\nif (u) u.name;\n```\n\n### Connection model\n\nIf you are using `mongoose.createConnection(...)`, you can pass a `\u003cmongoose.Connection\u003e` as the 6th parameter of `typedModel`. Then the module will be added to that connection instead.  \n(**Note:** If using the `connection` parameter, the `skipInit` parameter will not be used)\n\n```ts\nimport mongoose from 'mongoose'\nimport { typedModel } from 'ts-mongoose'\n\nconst UserSchema = createSchema({\n  name: Type.string({ required: true }),\n  age: Type.number({ required: true }),\n});\n\nconst connection = mongoose.createConnection(`mongodb://localhost:27017/test`, {...})\n\nconst User = typedModel('User', UserSchema, undefined, undefined, undefined, connection);\n\n\nconsole.log(connection.modelNames()) // Prints: [ 'User' ]\n\n// Now you can use the model directly\nUser.find({ name: 'Peter' })\n// Or through the connection\nconnection.model('User').find({ name: 'Peter' })\n\n```\n\n### TODO\n\n- support types: Map\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flstkz%2Fts-mongoose","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flstkz%2Fts-mongoose","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flstkz%2Fts-mongoose/lists"}