-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for enum
string assignments
#273
Comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Problem Currently we only support enum Color {
Red
Teal
} These enums are represented as integers. Occasionally you'd like to store a custom value in the database. Something like: enum Color {
Red = "RED"
Teal = "TEAL"
} This issue is about that. |
This comment has been minimized.
This comment has been minimized.
The data I get back from an api is an string enum with a dash separator (e.g. Looks like string assignment would solve that (I see that the generated typescript type is a dictionary const anyway) |
@matthewmueller here have a example. my project with access control as permission control. This lib use string as action store to database. And UseRoles decorator action also is string. |
+1 for string assignment |
Any word on this? |
+1 |
+1 for string assignment. For me, having a consistent set of string values which are used at every level of the stack make debugging much easier for me. If I am on the front end and I am setting a value on the enum, it is much nicer to have an explicit string value that I can manually check against the backend, and even further back against the data storage layer. Another thing that I prefer with strings vs the default enum behavior is that I don't know what the actual underlying values are. This compounds when I want to later on edit the enum and add more fields to it. Does it auto-increment? UUID? Does ordering matter? Lots of small things to think about there which can add unnecessary complexity. https://www.postgresql.org/docs/9.1/datatype-enum.html btw, this is my first day using prisma and I am loving it so far--thank you to the team for making it! |
This comment has been minimized.
This comment has been minimized.
+1 I have a use-case where I am creating a content-type enum. enum ContentType {
video_mp4 = "video/mp4"
} |
+1 for this feature, I currently have to do a boatload of type gymnastics to keep my string enums happy |
This comment was marked as off-topic.
This comment was marked as off-topic.
Any updates? |
1 similar comment
Any updates? |
A use-case I haven't seen mentioned here and likely falls into the same feature (apologies if I missed it): I have the need to assign specific integers to my enum, e.g. enum MyEnum {
Empty = 0,
SomeValue = 10,
SomeOtherValue = 11,
SomeFurtherValue = 20,
// ... many more entries
} This enum is shared across systems, and would love to weave it into some strongly-typed database schema! |
I could really use this feature |
I'd love to have this feature as well as I needed compatibility with tables created by SQLAlchemy, which use strings by default |
I'm really looking forward this feature being implemented |
Well, I'm going to mention my progress, and perhaps it will help the next person take the next step. I used as a starting point the script made by @PinkFromTheFuture. Basically, it creates a file ( Example: // prisma/schema.prisma
enum ErrorCode {
DEPENDENT_ERROR @map("d1")
POLICY_TEETH_EMPTY @map("p1")
POLICY_HEALTH_EMPTY @map("p2")
POLICY_HEALTH_UPDATED @map("p3")
POLICY_TEETH_UPDATED @map("p4")
POLICIES_EMPTY @map("p5")
} // prisma/enums.ts
export enum ErrorCode {
DEPENDENT_ERROR = "d1",
POLICY_TEETH_EMPTY = "p1",
POLICY_HEALTH_EMPTY = "p2",
POLICY_HEALTH_UPDATED = "p3",
POLICY_TEETH_UPDATED = "p4",
POLICIES_EMPTY = "p5",
} In other words, in the schema file, you can define any name for your keys, and in @Map, you map the values that are in your database. The problem I detected: When you define a column of a model in the Prisma schema using enums, Prisma generates something like this in your definition file: // node_modules/.prisma/client/index.d.ts
export const ErrorCode: {
DEPENDENT_ERROR: 'DEPENDENT_ERROR',
POLICY_TEETH_EMPTY: 'POLICY_TEETH_EMPTY',
POLICY_HEALTH_EMPTY: 'POLICY_HEALTH_EMPTY',
POLICY_HEALTH_UPDATED: 'POLICY_HEALTH_UPDATED',
POLICY_TEETH_UPDATED: 'POLICY_TEETH_UPDATED',
POLICIES_EMPTY: 'POLICIES_EMPTY', Consequently, trying to use enums (via What I thought: To write a script that uses the values generated by enums and updates the Prisma file with the mapped values. Then the Prisma file looks like this: // node_modules/.prisma/client/index.d.ts
export const ErrorCode: {
DEPENDENT_ERROR: 'd1',
POLICY_TEETH_EMPTY: 'p1',
POLICY_HEALTH_EMPTY: 'p2',
POLICY_HEALTH_UPDATED: 'p3',
POLICY_TEETH_UPDATED: 'p4',
POLICIES_EMPTY: 'p5',
} From there, code errors no longer occur. You can use the values as enums normally until... until when saving to the database, some Prisma function, it seems, expected to receive the string in the old format. For example, the key DEPENDENT_ERROR should pass the string 'DEPENDENT_ERROR' instead of 'd1', which is the value the database expects. This function that expects the old value instead of the new updated value for the key is the last obstacle for the hackfix to work. I will leave below the source code of the solution as far as I have come. To make it work, just add to your package.json script that runs Prisma Generate: // package.json
"scripts": {
"prisma:update-client": "prisma generate && ts-node prisma/utils/replace-enum-values.ts",
(...)
}
Source code:// prisma/utils/replace-enum-values.ts
import * as path from 'path';
import * as fs from 'fs/promises';
class EnumProcessor {
private enumsFilePath: string;
private prismaFilePath: string;
private schemaPath: string = 'prisma/schema.prisma';
constructor({ enumsFilePath, prismaFilePath }: { enumsFilePath: string; prismaFilePath: string }) {
this.prismaFilePath = prismaFilePath;
this.enumsFilePath = enumsFilePath;
}
async generateEnumsFile(): Promise<void> {
try {
const data: string = await fs.readFile(this.schemaPath, 'utf8');
const enumRegex: RegExp = /enum\s+(\w+)\s+\{([\s\S]+?)\}/g;
let match;
let allEnumsOutput: string = '';
while ((match = enumRegex.exec(data)) !== null) {
const enumName: string = match[1];
const enumBody: string = match[2];
const enumItemRegex: RegExp = /(\w+)\s+@map\((.+)\)/g;
let enumItems: string = '';
let itemMatch;
while ((itemMatch = enumItemRegex.exec(enumBody)) !== null) {
const itemName: string = itemMatch[1];
const itemValue: string = itemMatch[2].trim();
enumItems += ` ${itemName} = ${itemValue},\n`;
}
const enumOutput: string = `export enum ${enumName} {\n${enumItems}}\n\n`;
allEnumsOutput += enumOutput;
}
const absolutePath: string = path.resolve(__dirname, this.enumsFilePath);
await fs.writeFile(absolutePath, allEnumsOutput, 'utf8');
console.log(`Generated ${absolutePath}`);
} catch (err) {
console.error('Error processing enums file:', err);
}
}
processEnums(): [string, string][] {
const absolutePath: string = path.resolve(__dirname, this.enumsFilePath);
const enums: Record<string, Record<string, any>> = require(absolutePath);
const enumsPairs: [string, string][] = [];
function formatEnumAsString(enumName: string, enumObject: Record<string, any>): string {
const keysAndValues: string = Object.entries(enumObject)
.map(([key, value]) => `${key}: '${value}'`)
.join(',\n ');
return `export const ${enumName}: {\n ${keysAndValues}\n};\n`;
}
for (const enumName in enums) {
if (Object.prototype.hasOwnProperty.call(enums, enumName)) {
const enumObject: Record<string, any> = enums[enumName];
if (typeof enumObject === 'object' && !Array.isArray(enumObject) && Object.keys(enumObject).length > 0) {
const enumString: string = formatEnumAsString(enumName, enumObject);
enumsPairs.push([enumName, enumString]);
}
}
}
return enumsPairs;
}
async getConstantText(constantName: string): Promise<string> {
const absolutePath: string = path.resolve(__dirname, this.prismaFilePath);
const fileContent: string = await fs.readFile(absolutePath, 'utf-8');
const regex: RegExp = new RegExp(`export const ${constantName}: {[\\s\\S]*?};`);
const match: RegExpMatchArray | null = regex.exec(fileContent);
if (match) {
return match[0];
} else {
console.error(`Constant ${constantName} not found.`);
return '';
}
}
async replaceTextInFile(searchText: string, replaceText: string): Promise<void> {
const absolutePath: string = path.resolve(__dirname, this.prismaFilePath);
let fileContent: string = await fs.readFile(absolutePath, 'utf-8');
if (fileContent.includes(searchText)) {
fileContent = fileContent.replace(searchText, replaceText);
await fs.writeFile(absolutePath, fileContent);
} else {
console.error(`Text '${searchText}' not found in the file.`);
}
}
async processAndReplaceEnums(): Promise<void> {
await this.generateEnumsFile();
const enumsPairs: [string, string][] = this.processEnums();
for (const [enumName, enumString] of enumsPairs) {
const constCurrentText: string = await this.getConstantText(enumName);
await this.replaceTextInFile(constCurrentText, enumString);
}
console.log('Enums successfully updated in the Prisma file');
}
}
new EnumProcessor({
enumsFilePath: '../enums.ts',
prismaFilePath: './../../node_modules/.pnpm/@prisma+client@5.2.0_prisma@5.2.0/node_modules/.prisma/client/index.d.ts',
}).processAndReplaceEnums();
Well, I hope this helps the next person who manages to create a hackfix that will solve the problem when saving values to the database or, even better, inspire the Prisma team to implement this feature definitively. |
enum PurchaseStatus {
NotPurchased = "NotPurchased"
Purchased = "Purchased"
Delivering = "Delivering"
Delivered = "Delivered"
} please add feature for using string values with enum values :) |
4 and 1/2 years, and prisma still does not support more flexible enum declarations by default in my case, I'm trying to store enum values as integers to indiciate a type as their "labels" may change at any given time, and needing to update all stored values in the database is inconvenient enum SomeType {
Primary = 0
Secondary = 1
// and so on...
} which would hopefully generate a matching typescript enum |
This issue has been the determining reason to take my startup from nodejs. Honestly I have no words for this. We rewrote over 250k lines to golang after I was following this thread for half a year. This issue very strongly represents the community mess NodeJS brings along. 10.000 badly designed open source packages made by mediocre developers. I’m going to unsubscribe now. Good luck 😕 p.s. guys Golang development will reintroduce you to the pleasure it is to code. |
For me works this way:
|
@israelins85 does that actually work or is that a proposal you're making? |
this does not work for me (prisma 5.10.2 & postgres) |
works on MySql... |
MySQL |
From what I can see this approach does not work if your enum map values are kebab case e.g. Example in schema.prisma:
Prisma client generated output:
|
Here is a use case and I am open to any argument, why this is not a good one. Almost any data is also to be displayed. Enums are not an exception. For instance, in a web application form, this could a choice between user types, or payment methods etc. Having an associated label embedded to the keys is quite practical. In some cases these could be quite long as well. I think supporters of string assignment are thinking something inline of this. |
For those of us just wanting this for display purposes as @SarenT suggests above, a simple solution i've gone for is to just establish a pattern of your own in your enums like replace spaces with underscores and ampersands with the word "and", and when you display these to users run it through a helper function 🤷
|
My use case is representing Stripe subscription statuses in the database without manually mapping them from the string value to the enum value e.g.
Instead, I have to map the stripe value to the enum value. The @Map() doesn't work for me, at least it's not in the client generation (postgres) |
Is there any update to this feature? |
I whant int value in enum Like enum Resultions { |
What I'm about to propose may make things more complicated, but I came across this thread seeking a way to include an optional description to every enum value. Enum values, more often than not, need to be relatively short While many values are self-explanatory, others may not. For just a quick and dirty example, say I am managing a company shift schedule. I create a This enum lists values like Ideally, this is to indicate an Unnotified Abscene. Now, I already know many of you are gonna suggest why not just have a value for each of the Scheduled Absences i just listed. Yes, that would be the best option, and I have done just that for similar situations. However, when you take user experience and human nature into account, where a busy manager has to select from a dropdown list—a list that should be sorted alphabetically— Yes, I know there are several other fixes for this without having a description field for this, such as changing the I'm thinking the most straightforward solution may be to create a specialized "enum model" and the ability to create a I just know there will always be a vast variety of enum-related edge cases, so I figured the best solution would be a dynamic and versatile one. |
+1 |
The text was updated successfully, but these errors were encountered: