
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
prisma-prefixed-ids
Advanced tools
A Prisma extension that automatically adds prefixed IDs to your models. This package allows you to configure custom prefixes for your models and even customize how the IDs are generated.
npm install prisma-prefixed-ids
import { type Prisma, PrismaClient } from "@prisma/client";
import { extendPrismaClient } from 'prisma-prefixed-ids';
type ModelName = Prisma.ModelName;
// NOTE: if your Prisma.ModelName is not available in your setup,
// simply use the following instead:
// type ModelName = string;
// Create your Prisma client
const prisma = new PrismaClient();
// Define your model prefixes as an object
const prefixes: Partial<Record<ModelName, string>> = {
Organization: 'org',
User: 'usr',
// Add more model prefixes as needed
};
// Extend the client with prefixed IDs
const extendedPrisma = extendPrismaClient(prisma, {
prefixes,
});
// NOTE: if typing is an issue, simply override the
// return type to be the original `PrismaClient`:
// const extendedPrisma = extendPrismaClient(prisma, {
// prefixes,
// }) as unknown as PrismaClient
// Use the extended client
const organization = await extendedPrisma.organization.create({
data: {
name: 'My Organization',
// id will be automatically generated with prefix 'org_'
},
});
console.log(organization.id); // e.g., 'org_abc123...'
You can also use a function to determine prefixes dynamically. This is useful when you need conditional logic or want to compute prefixes based on the model name:
import { type Prisma, PrismaClient } from "@prisma/client";
import { extendPrismaClient } from 'prisma-prefixed-ids';
type ModelName = Prisma.ModelName;
const prisma = new PrismaClient();
// Define prefixes using a function
const extendedPrisma = extendPrismaClient(prisma, {
prefixes: (modelName: ModelName): string | null => {
// Return prefix for known models, null for unknown models
switch (modelName) {
case 'Organization':
return 'org';
case 'User':
return 'usr';
case 'Post':
return 'pst';
case 'Comment':
return 'cmt';
default:
return null; // No prefix for unknown models
}
},
});
// Or use a more dynamic approach
const extendedPrismaDynamic = extendPrismaClient(prisma, {
prefixes: (modelName: ModelName): string | null => {
// Convert model name to lowercase prefix
const prefix = modelName.toLowerCase().slice(0, 3);
// Only apply to certain models
const allowedModels = ['User', 'Post', 'Comment'];
return allowedModels.includes(modelName) ? prefix : null;
},
});
// Use the extended client
const user = await extendedPrisma.user.create({
data: {
name: 'John Doe',
// id will be automatically generated with prefix 'usr_'
},
});
console.log(user.id); // e.g., 'usr_abc123...'
Note: When using function-based prefixes, return null for models that should not have prefixed IDs. The extension will skip ID generation for those models.
Backward Compatibility: The function-based prefix feature is fully backward compatible. Existing code using object-based prefixes will continue to work without any changes.
Since version 1.5.0, this package fully supports nested writes with automatic ID generation for all related records. This includes complex relationship operations like:
// Create a user with nested posts
const userWithPosts = await extendedPrisma.user.create({
data: {
name: 'John Doe',
email: 'john@example.com',
posts: {
create: [
{
title: 'My First Post',
content: 'Hello world!',
published: true,
},
{
title: 'Draft Post',
content: 'Work in progress...',
published: false,
},
],
},
},
include: { posts: true },
});
// Result:
// - User gets ID: usr_abc123...
// - Posts get IDs: pst_def456..., pst_ghi789...
// Create deeply nested structures with automatic ID generation
const complexUser = await extendedPrisma.user.create({
data: {
name: 'Jane Smith',
email: 'jane@example.com',
posts: {
create: {
title: 'Post with Categories and Comments',
content: 'A comprehensive post...',
published: true,
categories: {
create: {
name: 'Technology',
description: 'Tech-related posts',
},
},
comments: {
createMany: {
data: [
{ content: 'Great post!', authorName: 'Reader 1' },
{ content: 'Very informative', authorName: 'Reader 2' },
],
},
},
},
},
},
include: {
posts: {
include: {
categories: true,
comments: true,
},
},
},
});
// All related records automatically get prefixed IDs:
// - User: usr_...
// - Post: pst_...
// - Category: cat_...
// - Comments: cmt_..., cmt_...
// Update existing records and create new related records
const updatedUser = await extendedPrisma.user.update({
where: { id: 'usr_existing123' },
data: {
name: 'Updated Name',
posts: {
create: [
{
title: 'New Post After Update',
content: 'Added after user update',
},
],
},
},
include: { posts: true },
});
// Existing user keeps original ID, new posts get fresh prefixed IDs
// Upsert with nested creates
const upsertedUser = await extendedPrisma.user.upsert({
where: { email: 'maybe@example.com' },
create: {
name: 'New User',
email: 'maybe@example.com',
posts: {
create: {
title: 'First Post',
content: 'Created during upsert',
},
},
},
update: {
name: 'Updated Existing User',
},
include: { posts: true },
});
// IDs are generated only for the create branch if record doesn't exist
// Connect to existing or create new with automatic ID generation
const postWithCategories = await extendedPrisma.post.create({
data: {
title: 'Post with Mixed Categories',
content: 'Some categories exist, others will be created',
categories: {
connectOrCreate: [
{
where: { name: 'Existing Category' },
create: { name: 'Should not be created' },
},
{
where: { name: 'New Category' },
create: {
name: 'New Category',
description: 'Freshly created category',
},
},
],
},
},
include: { categories: true },
});
// New categories get prefixed IDs, existing ones are connected as-is
The extension supports all Prisma nested write operations:
create - Single nested record creationcreateMany - Multiple nested records creationconnectOrCreate - Connect existing or create new recordsupsert - Update existing or create new recordsAll nested records that are created (not connected to existing ones) will automatically receive prefixed IDs according to your configuration.
You can customize how IDs are generated by providing your own ID generator function:
import { extendPrismaClient } from 'prisma-prefixed-ids';
import { customAlphabet } from 'nanoid';
// Create a custom ID generator
const customIdGenerator = (prefix: string) => {
const nanoid = customAlphabet('1234567890abcdef', 10);
return `${prefix}_${nanoid()}`;
};
// With object-based prefixes
const extendedPrisma = extendPrismaClient(prisma, {
prefixes: {
Organization: 'org',
User: 'usr',
},
idGenerator: customIdGenerator,
});
// With function-based prefixes
const extendedPrismaWithFunction = extendPrismaClient(prisma, {
prefixes: (modelName) => {
if (modelName === 'Organization') return 'org';
if (modelName === 'User') return 'usr';
return null;
},
idGenerator: customIdGenerator,
});
The extension accepts the following configuration:
prefixes: Either an object mapping model names to prefixes, or a function that returns a prefix for a given model name (required)idGenerator: A function that generates IDs (optional, defaults to using nanoid)The prefixes configuration can be provided in two ways:
A simple object where:
Example:
const prefixes = {
Organization: 'org',
User: 'usr',
Post: 'post',
Comment: 'cmt',
};
A function that takes a model name and returns a prefix string or null:
modelName: ModelName as parameterstring | null:
string prefix for models that should have prefixed IDsnull for models that should not have prefixed IDsExample:
const prefixes = (modelName: ModelName): string | null => {
// Simple mapping
const prefixMap: Record<string, string> = {
Organization: 'org',
User: 'usr',
Post: 'pst',
Comment: 'cmt',
};
return prefixMap[modelName] ?? null;
};
// Or with conditional logic
const prefixes = (modelName: ModelName): string | null => {
if (modelName.startsWith('System')) {
return 'sys'; // All system models get 'sys' prefix
}
if (modelName === 'User' || modelName === 'Organization') {
return modelName.toLowerCase().slice(0, 3);
}
return null; // Other models get no prefix
};
When to use function-based prefixes:
The idGenerator function should:
The default generator uses nanoid with a 24-character length and alphanumeric characters.
This package uses nanoid for ID generation instead of UUID v4 for several reasons:
Better Collision Resistance: While UUID v4 has a 122-bit random component, nanoid with 24 characters (using 36 possible characters) provides approximately 128 bits of entropy, making it even more collision-resistant than UUID v4.
Smaller Size: A UUID v4 is 36 characters long (including hyphens), while a nanoid with 24 characters is more compact. When combined with a prefix (e.g., usr_), the total length is still shorter than a UUID v4.
URL-Safe: nanoid uses URL-safe characters by default, making it suitable for use in URLs without encoding.
Customizable: nanoid allows you to customize the alphabet and length, giving you more control over the ID format.
Better Performance: nanoid is optimized for performance and generates IDs faster than UUID v4.
For example, with a 24-character nanoid:
usr_abc123DEF...)This project includes comprehensive unit and integration tests:
# Run all tests
npm test
# Run only unit tests (fast, no database)
npm run test:unit
# Run only integration tests (uses real SQLite database)
npm run test:integration
The integration tests automatically download the Prisma query engine and set up a SQLite database as needed. No additional setup is required.
For more detailed development information, see DEVELOPMENT.md.
MIT
FAQs
A Prisma extension that adds prefixed IDs to your models
The npm package prisma-prefixed-ids receives a total of 2,835 weekly downloads. As such, prisma-prefixed-ids popularity was classified as popular.
We found that prisma-prefixed-ids demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.