Skip to main content

strapi-utils

Page summary:

The @strapi/utils package provides shared helper functions used across Strapi's core and available for use in custom code. It includes error classes, environment variable helpers, hook factories, type parsing, string and file utilities, and async helpers.

The @strapi/utils package (import { ... } from '@strapi/utils') contains utility functions that Strapi uses internally but that you can also use in your own controllers, services, policies, middlewares, and lifecycle hooks.

Finding what you need

Sections on this page are organized alphabetically by export name. Use the table of contents on the right to jump directly to the utility you need.

Note

The error classes section of this page expands on the error handling documentation found in the dedicated Error handling page.

async

The async namespace provides asynchronous utility functions. It is imported as follows:

const { async } = require('@strapi/utils');

The following functions are available:

FunctionDescription
async.map(iterable, mapper, options?)Parallel map using p-map. Set concurrency in options to control parallelism.
async.pipe(...fns)Compose functions: the first function runs with the original arguments, each subsequent function receives the previous return value. Returns a Promise.
async.reduce(array)(iteratee, initialValue?)Asynchronous reduce over an array. Called in 2 steps: first pass the array, then pass the iteratee and optional initial value. The iteratee receives (accumulator, item, index).

The following example uses pipe to compose async functions, and reduce to accumulate values:

const { async: asyncUtils } = require('@strapi/utils');

// Compose async functions into a pipeline
const result = await asyncUtils.pipe(
fetchUser,
enrichWithProfile,
formatResponse
)(userId);

// Reduce an array asynchronously (note the curried call)
const total = await asyncUtils.reduce([1, 2, 3])(
async (sum, n) => sum + n,
0
); // 6

contentTypes

The contentTypes namespace exposes constants and helper functions for working with Strapi content-type schemas. It is imported as follows:

const { contentTypes } = require('@strapi/utils');

Constants

The following constants are available:

ConstantValueDescription
ID_ATTRIBUTE'id'Primary key field name
DOC_ID_ATTRIBUTE'documentId'Document identifier field name
PUBLISHED_AT_ATTRIBUTE'publishedAt'Publication timestamp field name
FIRST_PUBLISHED_AT_ATTRIBUTE'firstPublishedAt'First publication timestamp field name
CREATED_BY_ATTRIBUTE'createdBy'Creator reference field name
UPDATED_BY_ATTRIBUTE'updatedBy'Last editor reference field name
CREATED_AT_ATTRIBUTE'createdAt'Creation timestamp field name
UPDATED_AT_ATTRIBUTE'updatedAt'Update timestamp field name
SINGLE_TYPE'singleType'Single type kind identifier
COLLECTION_TYPE'collectionType'Collection type kind identifier

Attribute inspection functions

The following functions check the type of a single attribute:

FunctionDescription
isComponentAttribute(attribute)Check if the attribute is a component or a dynamic zone (returns true for both; use isDynamicZoneAttribute to distinguish)
isDynamicZoneAttribute(attribute)Check if the attribute is a dynamic zone
isMediaAttribute(attribute)Check if the attribute is a media field
isMorphToRelationalAttribute(attribute)Check if the attribute is a morph-to relation
isRelationalAttribute(attribute)Check if the attribute is a relation
isScalarAttribute(attribute)Check if the attribute is a scalar value
isTypedAttribute(attribute, type)Check if the attribute has a specific type

Schema inspection functions

The following functions inspect an entire content-type schema:

FunctionDescription
getCreatorFields(schema)Return creator fields present in the schema (createdBy, updatedBy)
getNonWritableAttributes(schema)Return field names that cannot be written to
getScalarAttributes(schema)Return attributes that are scalar values
getTimestamps(schema)Return timestamp fields present in the schema (createdAt, updatedAt)
getVisibleAttributes(schema)Return schema attributes that are not marked as non-visible
getWritableAttributes(schema)Return field names that can be written to
hasDraftAndPublish(schema)Check if the schema has draft and publish enabled
isWritableAttribute(schema, attributeName)Check if a specific attribute is writable

The following example iterates over a content type's attributes to find relations and writable fields:

const { contentTypes } = require('@strapi/utils');

const articleSchema = strapi.contentType('api::article.article');

// List all relation fields
for (const [name, attribute] of Object.entries(articleSchema.attributes)) {
if (contentTypes.isRelationalAttribute(attribute)) {
console.log(`${name} is a relation`);
}
}

// Get only the fields that can be written to
const writableFields = contentTypes.getWritableAttributes(articleSchema);

// Check if draft and publish is enabled
if (contentTypes.hasDraftAndPublish(articleSchema)) {
console.log('This content type supports drafts');
}

env

A helper function to read environment variables with type-safe parsing. The env function returns the raw string value, while its methods parse the value to a specific type. It is imported as follows:

const { env } = require('@strapi/utils');
// or in TypeScript: import { env } from '@strapi/utils';

The env helper can be called directly or with the following typed methods:

MethodReturn typeDescription
env(key)string | undefinedReturn the raw value
env(key, default)stringReturn the raw value or the default
env.array(key, default?)string[] | undefinedSplit by comma, trim values, strip surrounding [] and double quotes
env.bool(key, default?)boolean | undefined'true' returns true, anything else returns false
env.date(key, default?)Date | undefinedParse with new Date()
env.float(key, default?)number | undefinedParse as float (parseFloat)
env.int(key, default?)number | undefinedParse as integer (parseInt)
env.json(key, default?)object | undefinedParse as JSON; throws an Error with a descriptive message on invalid JSON
env.oneOf(key, expectedValues, default?)string | undefinedReturn the value only if it matches one of expectedValues, otherwise return default. Throws if expectedValues is not provided or if default is not itself in expectedValues.

The following example shows how to use env helpers in a server configuration file:

/config/server.js
const { env } = require('@strapi/utils');

module.exports = {
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
app: {
keys: env.array('APP_KEYS'),
},
};

errors

Custom error classes that extend the Node.js Error class. All errors share a common structure:

PropertyTypeDescription
namestringError class name (e.g., 'ApplicationError', 'ValidationError')
messagestringHuman-readable error message
detailsobjectAdditional error context

The error classes are imported as follows:

const { errors } = require('@strapi/utils');
// or in TypeScript: import { errors } from '@strapi/utils';

The following error classes are available:

Error classDefault messageDetails default
ApplicationError'An application error occurred'{}
ValidationError(required)depends on constructor input
YupValidationError'Validation' (or formatted Yup message){ errors: [] }
PaginationError'Invalid pagination'depends on constructor input
NotFoundError'Entity not found'depends on constructor input
ForbiddenError'Forbidden access'depends on constructor input
UnauthorizedError'Unauthorized'depends on constructor input
RateLimitError'Too many requests, please try again later.'{}
PayloadTooLargeError'Entity too large'depends on constructor input
PolicyError'Policy Failed'{}
NotImplementedError'This feature is not implemented yet'depends on constructor input

PolicyError extends ForbiddenError. All other error classes extend ApplicationError.

The following example shows how to throw errors in a service and a policy:

const { errors } = require('@strapi/utils');

// In a service or lifecycle hook
throw new errors.ApplicationError('Something went wrong', { foo: 'bar' });

// In a policy
throw new errors.PolicyError('Access denied', { policy: 'is-owner' });
Tip

Use ApplicationError when throwing errors in model lifecycle hooks so that meaningful messages display in the admin panel. See the Error handling page for more examples.

file

The file namespace provides helpers for working with streams and file sizes. It is imported as follows:

const { file } = require('@strapi/utils');

The following functions are available:

FunctionReturn typeDescription
bytesToHumanReadable(bytes)stringFormat bytes as a human-readable string (e.g., '2 MB')
bytesToKbytes(bytes)numberConvert bytes to kilobytes (rounded to 2 decimals)
getStreamSize(stream)Promise<number>Calculate the total size of a stream in bytes
kbytesToBytes(kbytes)numberConvert kilobytes to bytes
streamToBuffer(stream)Promise<Buffer>Convert a readable stream into a Buffer
writableDiscardStream(options?)WritableCreate a writable stream that discards all data

The following example converts an uploaded stream to a buffer and logs its size:

const { file } = require('@strapi/utils');

const buffer = await file.streamToBuffer(uploadStream);
const sizeInKb = file.bytesToKbytes(buffer.length);
console.log(`Uploaded ${file.bytesToHumanReadable(buffer.length)} (${sizeInKb} KB)`);

hooks

Factory functions to create hook registries. Hooks let you register handler functions and execute them in different patterns. The namespace is imported as follows:

const { hooks } = require('@strapi/utils');

Each hook instance exposes the following 4 methods:

MethodDescription
register(handler)Add a handler function to the hook
delete(handler)Remove a previously registered handler
getHandlers()Return the list of registered handlers
call(...args)Execute registered handlers according to the hook type

Available hook factories

The following factory functions create different hook types. Use series when handlers must run in order, waterfall when each handler transforms data for the next, parallel when handlers are independent and can run concurrently, and bail when you need the first handler that returns a value to short-circuit the rest:

FactoryExecution pattern
hooks.createAsyncSeriesHook()Execute handlers sequentially with the same context
hooks.createAsyncSeriesWaterfallHook()Execute handlers sequentially, passing each return value to the next handler
hooks.createAsyncParallelHook()Execute all handlers concurrently
hooks.createAsyncBailHook()Execute handlers sequentially, stop at the first handler that returns a non-undefined value

The following example registers and calls handlers with a series hook :

const { hooks } = require('@strapi/utils');

const myHook = hooks.createAsyncSeriesHook();

myHook.register(async (context) => {
console.log('First handler', context);
});

myHook.register(async (context) => {
console.log('Second handler', context);
});

// Execute all handlers in order
await myHook.call({ data: 'example' });

pagination

The pagination namespace provides helpers for handling pagination parameters. It is imported as follows:

const { pagination } = require('@strapi/utils');

The following functions are available:

FunctionDescription
transformOffsetPaginationInfo(params, total)Transform pagination data into { start, limit, total } format
transformPagedPaginationInfo(params, total)Transform pagination data into { page, pageSize, pageCount, total } format
withDefaultPagination(params, options?)Apply default values and validate pagination parameters (see details below)

The withDefaultPagination function supports both page/pageSize and start/limit formats. It accepts an optional options object with the following properties:

OptionTypeDescription
defaultsobjectOverride the initial pagination values for each format (e.g., { page: { pageSize: 25 } })
maxLimitnumberCap the limit or pageSize value. Set to -1 for no cap.

The following example applies default pagination and transforms the result:

const { pagination } = require('@strapi/utils');

const params = pagination.withDefaultPagination({ page: 2 }, { maxLimit: 100 });
const info = pagination.transformPagedPaginationInfo(params, 250);
// { page: 2, pageSize: 25, pageCount: 10, total: 250 }

parseType

Cast a value to a specific Strapi field type. The function is imported as follows:

const { parseType } = require('@strapi/utils');

The function accepts the following parameters:

ParameterTypeDescription
typestringTarget type: 'boolean', 'integer', 'biginteger', 'float', 'decimal', 'time', 'date', 'timestamp', or 'datetime'
valueunknownThe value to parse
forceCastbooleanForce conversion for booleans. Default: false

The return value depends on the target type:

TypeReturn typeFormat
booleanbooleanAccepts 'true', 't', '1', 1 as true
integer, biginteger, float, decimalnumberNumeric conversion
timestringHH:mm:ss.SSS
datestringyyyy-MM-dd
timestamp, datetimeDateDate object

The following example demonstrates parsing different field types:

parseType({ type: 'boolean', value: 'true' }); // true
parseType({ type: 'integer', value: '42' }); // 42
parseType({ type: 'date', value: '2024-01-15T10:30:00Z' }); // '2024-01-15'

policy

Helpers to create and manage policies. The namespace exposes 2 functions: createPolicy to define a policy handler with an optional configuration validator, and createPolicyContext to build a typed context object that the handler can inspect. The namespace is imported as follows:

const { policy } = require('@strapi/utils');

createPolicy

Create a policy with an optional configuration validator. The function accepts the following parameters:

ParameterTypeRequiredDescription
namestringNoPolicy name (defaults to 'unnamed')
handlerfunctionYesPolicy handler function
validatorfunctionNoValidate the policy configuration; throws on invalid config

The following example creates a policy with a configuration validator:

const myPolicy = policy.createPolicy({
name: 'is-owner',
validator: (config) => {
if (!config.field) throw new Error('Missing field');
},
handler: (ctx, config, { strapi }) => {
// policy logic
return true;
},
});

createPolicyContext

The createPolicyContext function creates a typed context object for use within a policy handler. It accepts a type string (e.g., 'admin' or 'koa') and the Koa context, and returns an object with an is() method and a type property:

const policyCtx = policy.createPolicyContext('admin', ctx);

policyCtx.is('admin'); // true
policyCtx.type; // 'admin'

primitives

Low-level data transformation helpers. The following sub-modules are available as direct top-level imports from @strapi/utils:

const { strings, objects, arrays, dates } = require('@strapi/utils');

strings

The following string utility functions are available:

FunctionDescription
strings.getCommonPath(...paths)Find the common path prefix from multiple file paths
strings.isCamelCase(value)Check if a string is in camelCase format
strings.isEqual(a, b)Compare 2 values as strings
strings.isKebabCase(value)Check if a string is in kebab-case format
strings.joinBy(separator, ...parts)Join strings with a separator, trimming duplicate separators at join points
strings.nameToCollectionName(name)Convert a name to a snake_case collection name
strings.nameToSlug(name, options?)Convert a name to a URL-friendly slug. Default separator: '-'
strings.startsWithANumber(value)Check if a string starts with a digit
strings.toKebabCase(value)Convert a string to kebab-case
strings.toRegressedEnumValue(value)Replace accented characters with their ASCII equivalents, then separate words with underscores to produce a string suitable for use as an enum key (preserves original casing)

objects

The following object utility function is available:

FunctionDescription
objects.keysDeep(obj)Return all nested keys in dot-notation (e.g., ['a.b', 'a.c'])

arrays

The following array utility function is available:

FunctionDescription
arrays.includesString(arr, val)Check if an array includes a value when both are compared as strings

dates

The following date utility function is available:

FunctionDescription
dates.timestampCode(date?)Convert a Date (defaults to new Date()) to a base-36 string of the millisecond timestamp

providerFactory

Create a pluggable registry that stores and retrieves items by key, with lifecycle hooks. This is the same factory Strapi uses internally for its upload and email providers. Use providerFactory when you need a store of interchangeable strategies or adapters in your own plugins. The factory is imported as follows:

const { providerFactory } = require('@strapi/utils');

Parameters

The factory accepts the following parameter:

ParameterTypeDefaultDescription
throwOnDuplicatesbooleantrueThrow an error when registering a key that already exists

Provider methods

The returned provider instance exposes the following methods:

MethodReturn typeDescription
register(key, item)Promise<Provider>Register an item. Triggers willRegister and didRegister hooks.
delete(key)Promise<Provider>Remove an item. Triggers willDelete and didDelete hooks.
get(key)T | undefinedRetrieve an item by key
values()T[]Return all registered items
keys()string[]Return all registered keys
has(key)booleanCheck if a key is registered
size()numberReturn the number of registered items
clear()Promise<Provider>Remove all items

Provider hooks

Each provider instance exposes a hooks object with 4 hook registries:

HookTypeTrigger
hooks.willRegisterAsync seriesBefore an item is registered
hooks.didRegisterAsync parallelAfter an item is registered
hooks.willDeleteAsync parallelBefore an item is deleted
hooks.didDeleteAsync parallelAfter an item is deleted

The following example creates a provider and registers an item with a lifecycle hook:

const { providerFactory } = require('@strapi/utils');

const registry = providerFactory();

registry.hooks.willRegister.register(async ({ key, value }) => {
console.log(`About to register: ${key}`);
});

await registry.register('my-provider', { execute: () => {} });

registry.get('my-provider'); // { execute: [Function] }
registry.has('my-provider'); // true
registry.size(); // 1

relations

The relations namespace provides helpers to inspect the cardinality of relation attributes (e.g., one-to-many vs. many-to-many). To check whether an attribute is a relation at all, use contentTypes.isRelationalAttribute instead. The namespace is imported as follows:

const { relations } = require('@strapi/utils');

The following functions are available:

FunctionDescription
getRelationalFields(contentType)Return all relation field names from a content type
isAnyToMany(attribute)Check for oneToMany or manyToMany relations
isAnyToOne(attribute)Check for oneToOne or manyToOne relations
isManyToAny(attribute)Check for manyToMany or manyToOne relations
isOneToAny(attribute)Check for oneToOne or oneToMany relations
isPolymorphic(attribute)Check for morphOne, morphMany, morphToOne, or morphToMany relations

The following example filters a content type's attributes to find all one-to-many or many-to-many relations:

const { relations, contentTypes } = require('@strapi/utils');

const schema = strapi.contentType('api::article.article');

for (const [name, attribute] of Object.entries(schema.attributes)) {
if (contentTypes.isRelationalAttribute(attribute) && relations.isAnyToMany(attribute)) {
console.log(`${name} is a *-to-many relation`);
}
}

sanitize

The sanitize namespace provides functions to clean input and output data based on content-type schemas. Use sanitize to remove disallowed, private, or restricted fields before processing or returning data.

Tip

In most controllers, you do not need to call sanitize directly. Strapi provides built-in sanitizeQuery and sanitizeOutput helpers that handle the setup for you (see Controllers documentation for details). Use the lower-level API below when you need sanitization outside of a controller context (e.g., in a service or a custom script).

The namespace is imported as follows:

const { sanitize } = require('@strapi/utils');

The createAPISanitizers function takes a model resolver and returns a set of sanitizer methods scoped to that model. A model resolver is a function that, given a content-type UID (e.g., 'api::article.article'), returns the corresponding schema. In practice, strapi.getModel already does this. You typically call createAPISanitizers once during bootstrap or at the top of a service:

const sanitizers = sanitize.createAPISanitizers({
getModel: strapi.getModel.bind(strapi),
});

The returned object provides the following:

MethodDescription
sanitizers.input(data, schema, options?)Sanitize request body data
sanitizers.output(data, schema, options?)Sanitize response data
sanitizers.query(query, schema, options?)Sanitize query parameters
sanitizers.filters(filters, schema, options?)Sanitize filter expressions
sanitizers.sort(sort, schema, options?)Sanitize sort parameters
sanitizers.fields(fields, schema, options?)Sanitize field selections
sanitizers.populate(populate, schema, options?)Sanitize populate directives

Each method accepts an optional options object with the following properties:

OptionTypeDefaultDescription
authobjectundefinedThe authentication object from the request (typically ctx.state.auth). When provided, relation fields the user does not have permission to access are removed from the output. When omitted, no permission-based filtering is applied.
strictParamsbooleanfalseWhen true, removes fields or query parameters not declared in the content-type schema. When false, unrecognized fields pass through.
routeobjectundefinedThe route object (typically ctx.route). When strictParams is true, the sanitizer reads the request key from the route configuration to know which custom query or body parameters are allowed beyond the core set. Has no effect when strictParams is false. See Routes for details on route configuration.

setCreatorFields

Set createdBy and updatedBy fields on an entity. Use this when building a custom controller or service that creates or updates entries outside of Strapi's default Document Service. The function returns a curried function : call it with options first, then with the entity data. It is imported as follows:

const { setCreatorFields } = require('@strapi/utils');

The function accepts the following parameters:

ParameterTypeDefaultDescription
user{ id: string | number }(required)The user performing the action
isEditionbooleanfalseIf true, only set updatedBy; if false, set both createdBy and updatedBy

The following example shows how to set creator fields on creation and update:

const { setCreatorFields } = require('@strapi/utils');

const addCreator = setCreatorFields({ user: { id: 1 } });
const data = addCreator({ title: 'My Article' });
// { title: 'My Article', createdBy: 1, updatedBy: 1 }

const updateCreator = setCreatorFields({ user: { id: 2 }, isEdition: true });
const updated = updateCreator(data);
// { title: 'My Article', createdBy: 1, updatedBy: 2 }

validate

The validate namespace provides functions to check input and query data against content-type schemas. Use validate to reject requests that reference unknown, private, or restricted fields.

Tip

Like sanitize, controllers already provide built-in validation helpers (validateQuery, validateInput). Use the lower-level API below when you need validation outside of a controller context.

The namespace is imported as follows:

const { validate } = require('@strapi/utils');

The createAPIValidators function takes a model resolver (see sanitize for details) and returns a set of validator methods scoped to that model:

const validators = validate.createAPIValidators({
getModel: strapi.getModel.bind(strapi),
});

The returned object provides the following:

MethodDescription
validators.input(data, schema, options?)Validate request body data
validators.query(query, schema, options?)Validate query parameters
validators.filters(filters, schema, options?)Validate filter expressions
validators.sort(sort, schema, options?)Validate sort parameters
validators.fields(fields, schema, options?)Validate field selections
validators.populate(populate, schema, options?)Validate populate directives

Each method accepts an optional options object with the same properties as the sanitize options: auth for permission-based checks, strictParams to reject unknown fields, and route to allow custom route parameters in strict mode.

The following example validates a query in a custom service and catches the error:

const { validate, errors } = require('@strapi/utils');

const validators = validate.createAPIValidators({
getModel: strapi.getModel.bind(strapi),
});

try {
await validators.query(ctx.query, 'api::article.article', {
auth: ctx.state.auth,
});
} catch (error) {
// error is a ValidationError with details about which fields failed
console.error(error.message, error.details);
}

yup

The yup namespace re-exports the Yup validation library with Strapi-specific extensions. It is imported as follows:

const { yup } = require('@strapi/utils');

Additional Yup methods

Strapi adds the following methods to Yup schemas:

MethodSchema typeDescription
yup.strapiID()CustomValidate a Strapi ID (string or non-negative integer)
.notNil()AnyEnsure value is not undefined or null
.notNull()AnyEnsure value is not null
.isFunction()MixedValidate that the value is a function
.isCamelCase()StringValidate camelCase format
.isKebabCase()StringValidate kebab-case format
.onlyContainsFunctions()ObjectValidate that all values in the object are functions
.uniqueProperty(property, message)ArrayValidate that a specific property is unique across array items

Schema validation helpers

validateYupSchema and validateYupSchemaSync are top-level exports from @strapi/utils, not part of the yup namespace:

const { validateYupSchema, validateYupSchemaSync } = require('@strapi/utils');

The following helper functions are available:

FunctionDescription
validateYupSchema(schema, options?)Return an async validator function (body, errorMessage?) => Promise for a Yup schema. Default options: { strict: true, abortEarly: false }.
validateYupSchemaSync(schema, options?)Return a synchronous validator function (body, errorMessage?) => result for a Yup schema. Default options: { strict: true, abortEarly: false }.

zod

Strapi re-exports the z instance from Zod and provides a validateZod helper that wraps a Zod schema into a Strapi-style validator. Strapi does not add custom methods to Zod. z is the standard Zod API. The helpers are imported as follows:

const { validateZod, z } = require('@strapi/utils');

The following example defines a schema and creates a validator function with validateZod. On success, the function returns the parsed data. On failure, it throws a ValidationError (see errors) with details about which fields failed:

const schema = z.object({
name: z.string().min(1),
age: z.number().positive(),
});

const validate = validateZod(schema);

const parsed = validate({ name: 'Alice', age: 30 }); // returns parsed data
validate({ name: '' }); // throws ValidationError