How to handle Enums in Mongoose with TypeScript
Mongoose doesn't have built-in support for TypeScript enums, but learn how this is possible, and how it helps enforce data integrity and provides type checking during development

On the journey of building GISU Rest API - initialy, built, just to handle posting at the School of Education, Gambia College - we want to ensure that only valid enums values can be assigned to certain fields. This helps us enforce data integrity and provides type checking during development.
But…
Mongoose doesn’t have built-in support for TypeScript enums, and it really can’t because TypeScript enums are just POJOs at runtime. But one thing we should do is eliminate the need for the Object.values()
part. (Mastering JS )
One of the enums we use, was to let students select the region of the school they want to be posted at. We don’t want them to type this data due the fact tha we will different inputs from the students. So enums to the rescue.
That said, let’s look at how we have implemented this:
Enum definition
First, we define the Enum:
// Region enum definition
enum ERegion {
r1 = 'Region 1',
r2 = 'Region 2',
r3 = 'Region 3',
r4 = 'Region 4',
r5 = 'Region 5',
r6 = 'Region 6',
}
Note the following:
We use the following naming convention as far as enums, interfaces and types are concern:
- interfaces start with capital
I
- types starts with capital
T
and - enums starts with a capital
E
This is why the name of the region enum is: ERegion
and not just Region
.
Use the enum in Mongoose schema
Second, we use the enum defined above in our schema.
import { Schema, model, Types } from 'mongoose';
// Omitted enum and interface definition
// Document schema definition
const regionSchema = new Schema<IRegion>(
{
// omitted codes
name: {
type: String,
required: true,
unique: true,
enum: Object.values(ERegion) // Set the enum values to the valid options from the ERegion enum
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
}
},
{ timestamps: true }
);
// Create a Model.
const Region = model<IRegion>('region', regionSchema);
// Export the model
export default Region;
Here’s that line: enum: Object.values(ERegion)
inside the name
object.
enums with a default value
In some of our cases, we have to use enums with a default value and here’s an example of that:
import { Schema, model, Types } from 'mongoose';
// instruction mode enum definition
enum EInstructionsMode {
lecture = 'Lecture',
posting = 'Posting',
}
// Omitted interface definition
// Document Schema
const courseSchema = new Schema<ICourse>(
{
// omitted codes
instructions_mode: {
type: String,
required: true,
enum: Object.values(EInstructionsMode),
default: EInstructionsMode.lecture // default to "Lecture"
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
}
},
{ timestamps: true }
);
// Create a Model.
const CourseModel = model<ICourse>('course', courseSchema);
// Export the model
export default CourseModel;
Here’s that line: default: EInstructionsMode.lecture
inside the instructions_mode
object.
Note: Enums in Mongoose are stored as strings in the database, so make sure to use string values when defining the enum options.
Extracting enums to their own module
When you are working on a medium or large scale project, we highly recommend you to extract all your enums in your code base to one enums directory.
Within the src
of your project create a folder called enums
and a file called index.ts
(if using typescript, otherwise, index.js)
This has the following benefits:
- Single source of truth
- Re-usability of enums winthin and across projects
- ….
As a general practice it’s good to have an index.js
file which exports the enums so you don’t have to change import paths or have repetitive ones.
For example if we are going to create an enums for regions and user roles. We can create the two files within the enums directory: regions.js
and user-roles.js
.
regions.js
// Region enum definition
enum ERegions {
r1 = 'Region 1',
r2 = 'Region 2',
r3 = 'Region 3',
r4 = 'Region 4',
r5 = 'Region 5',
r6 = 'Region 6',
}
export default ERegions;
user-roles.js
// User role enum definition
enum EUserRoles {
super_admin = 'Super Admin',
admin = 'Admin',
user = 'User',
}
export default EUserRoles;
Then finally, in the index.js
you can export them:
export { default as ERegions } from './ERegions';
export { default as EUserRoles } from './EUserRoles';
// more enums
And the beauty is, you can easily import enums with one line from the same drectory like this:
import { ERegions, EUserRoles } from '../enums'
Note: keep the component file with its name so you don’t get confused when you have multiple ones open.