| name | graphile-v5-inflection |
| description | Customize GraphQL naming and inflection in PostGraphile v5. Use when asked to "customize naming", "change field names", "simplify relation names", "remove schema prefix", "fix pluralization", or when GraphQL names don't match your conventions. |
| compatibility | PostGraphile v5+, graphile-config |
| metadata | {"author":"constructive-io","version":"1.0.0"} |
PostGraphile v5 Inflection
Customize how PostgreSQL names become GraphQL names using inflectors.
Official Documentation
When to Apply
Use this skill when:
- GraphQL field names don't match your naming conventions
- You want to simplify relation names (e.g.,
userByAuthorId -> author)
- You want to remove schema prefixes from type names
- You need custom pluralization rules
- You want to shorten mutation names
Understanding Inflectors
Inflectors are functions that transform names. PostGraphile v5 uses them to convert PostgreSQL names (snake_case) to GraphQL names (camelCase).
Key inflectors:
_attributeName - Column names
_schemaPrefix - Schema prefix for type names
singleRelation - Forward relation names (e.g., post.author)
_manyRelation - Backward relation names (e.g., user.posts)
rowByUnique - Query field names (e.g., user, userByEmail)
updateByKeysField - Update mutation names
deleteByKeysField - Delete mutation names
Creating an Inflector Plugin
import type { GraphileConfig } from 'graphile-config';
export const MyInflectorPlugin: GraphileConfig.Plugin = {
name: 'MyInflectorPlugin',
version: '1.0.0',
inflection: {
replace: {
myInflector(previous, options, details) {
const defaultResult = previous!(details);
return defaultResult;
},
},
},
};
Common Customizations
Remove Schema Prefix from All Schemas
By default, PostGraphile only removes the prefix for the first schema. Override to remove for all:
inflection: {
replace: {
_schemaPrefix(_previous, _options, _details) {
return '';
},
},
},
Source: https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgTablesPlugin.ts#L261-L271
Keep id Columns as id
Prevent renaming id to rowId:
inflection: {
replace: {
_attributeName(_previous, _options, details) {
const attribute = details.codec.attributes[details.attributeName];
const name = attribute?.extensions?.tags?.name || details.attributeName;
return this.coerceToGraphQLName(name);
},
},
},
Source: https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgAttributesPlugin.ts#L289-L298
Simplify Relation Names
Transform userByAuthorId to author:
inflection: {
replace: {
singleRelation(previous, _options, details) {
const { registry, codec, relationName } = details;
const relation = registry.pgRelations[codec.name]?.[relationName];
if (typeof relation.extensions?.tags?.fieldName === 'string') {
return relation.extensions.tags.fieldName;
}
if (relation.localAttributes.length === 1) {
const attributeName = relation.localAttributes[0];
const match = attributeName.match(/^(.+?)(_id|_uuid|Id|Uuid)$/);
if (match) {
return this.camelCase(match[1]);
}
}
return previous!(details);
},
},
},
Simplify Root Query Names
Transform allUsers to users:
inflection: {
replace: {
allRowsConnection(_previous, _options, resource) {
const resourceName = this._singularizedResourceName(resource);
return this.camelCase(this.pluralize(resourceName));
},
},
},
Shorten Primary Key Lookups
Transform userById to user:
inflection: {
replace: {
rowByUnique(previous, _options, details) {
const { unique, resource } = details;
if (typeof unique.extensions?.tags?.fieldName === 'string') {
return unique.extensions?.tags?.fieldName;
}
if (unique.isPrimary) {
return this.camelCase(this._singularizedCodecName(resource.codec));
}
return previous!(details);
},
},
},
Shorten Mutation Names
Transform updateUserById to updateUser:
inflection: {
replace: {
updateByKeysField(previous, _options, details) {
const { resource, unique } = details;
if (unique.isPrimary) {
return this.camelCase(`update_${this._singularizedCodecName(resource.codec)}`);
}
return previous!(details);
},
deleteByKeysField(previous, _options, details) {
const { resource, unique } = details;
if (unique.isPrimary) {
return this.camelCase(`delete_${this._singularizedCodecName(resource.codec)}`);
}
return previous!(details);
},
},
},
Complete Custom Inflector Plugin
import type { GraphileConfig } from 'graphile-config';
export const CustomInflectorPlugin: GraphileConfig.Plugin = {
name: 'CustomInflectorPlugin',
version: '1.0.0',
inflection: {
replace: {
_schemaPrefix(_previous, _options, _details) {
return '';
},
_attributeName(_previous, _options, details) {
const attribute = details.codec.attributes[details.attributeName];
const name = attribute?.extensions?.tags?.name || details.attributeName;
return this.coerceToGraphQLName(name);
},
allRowsConnection(_previous, _options, resource) {
const resourceName = this._singularizedResourceName(resource);
return this.camelCase(this.pluralize(resourceName));
},
rowByUnique(previous, _options, details) {
const { unique, resource } = details;
if (unique.isPrimary) {
return this.camelCase(this._singularizedCodecName(resource.codec));
}
return previous!(details);
},
updateByKeysField(previous, _options, details) {
const { resource, unique } = details;
if (unique.isPrimary) {
return this.camelCase(`update_${this._singularizedCodecName(resource.codec)}`);
}
return previous!(details);
},
deleteByKeysField(previous, _options, details) {
const { resource, unique } = details;
if (unique.isPrimary) {
return this.camelCase(`delete_${this._singularizedCodecName(resource.codec)}`);
}
return previous!(details);
},
},
},
};
export const CustomInflectorPreset: GraphileConfig.Preset = {
plugins: [CustomInflectorPlugin],
};
Using Smart Tags for Per-Table Overrides
Instead of global inflector changes, use smart tags for specific tables:
COMMENT ON TABLE users IS E'@name Person';
COMMENT ON COLUMN users.email_address IS E'@name email';
COMMENT ON CONSTRAINT posts_author_id_fkey ON posts IS E'@fieldName author';
Troubleshooting
| Issue | Solution |
|---|
| Inflector not called | Check plugin is in preset's plugins array |
previous is undefined | Use previous!() with non-null assertion |
| Type errors | Ensure proper typing for details parameter |
| Changes not visible | Schema is built once; restart server |
References