Skip to content

Upgrading

This page documents breaking changes, new features, and migration steps for releases that require your attention when upgrading. New versions are listed from newest to oldest.

v3 is a major release that removes long-standing deprecated APIs and tightens up naming consistency. It also ships a rebuilt UI layer based on shadcn/ui, background jobs for non-integration apps, and a new Announcement module. The sections below cover the new features, followed by each breaking change and its migration steps.

The built-in UI - project integration pages, files tree, settings modal, and JSON Schema forms - has been rebuilt on shadcn/ui with Crowdin design tokens, replacing the legacy Crowdin web components. Screens now render consistently across modules and follow the Crowdin theme (including dark mode) out of the box. No configuration changes are required; if you build a custom UI, use the same components via the Crowdin UI Registry so your app matches the built-in screens.

Any app can now schedule background jobs with crowdinApp.cron.schedule(expression, task) and iterate over every installed organization with crowdinApp.forEachCrowdinConnection, which supplies an authenticated Crowdin API client per installation, refreshes tokens, and isolates failures. Previously this was only available to project integrations via projectIntegration.cronJobs.

See the Background jobs guide for options (concurrency, checkSubscription), the result shape, and authentication limits.

The new Announcement module (announcement option) lets your app show a message to everyone in the organization, in the header alert or on a glossary page. Crowdin requests the announcements on each render and passes the reader’s context, so your app decides what to show per request. See the module page for placements, the announcement shape, and the request context.

The deprecated GET /api/jobs endpoint has been removed from both the integration module and the API module. Use GET /api/job-info instead.

RemovedReplacement
GET /api/jobs?jobId=<id>GET /api/job-info?jobId=<id>

The new endpoint requires jobId as a mandatory query parameter and returns richer job details (including createdAt, updatedAt, finishedAt as ISO strings, and type).

The PostgreSQL storage config field has been renamed from postgreConfig to postgresConfig to fix the missing s.

const configuration = {
postgreConfig: {
postgresConfig: {
host: 'localhost',
database: 'myapp',
},
};

The EditorMode.TRANSLATE enum member now has a lowercase value to be consistent with all other members.

MemberOld valueNew value
EditorMode.TRANSLATE'TRANSLATE''translate'

Migration: if your code compares against the string value directly, update it to 'translate'. If you use the enum member (EditorMode.TRANSLATE), no change is needed.

The duplicate EditorMode enum that was previously exported from @crowdin/app-project-module/modules/workflow-step-type has been removed. The canonical enum is the one exported from the main entrypoint.

import { EditorMode } from '@crowdin/app-project-module/modules/workflow-step-type';
import { EditorMode } from '@crowdin/app-project-module';

Note that the old enum in workflow-step-type used different member names (comfortable, sideBySide, multilingual) compared to the canonical enum (COMFORTABLE, SIDE_BY_SIDE, MULTILINGUAL). Update any references accordingly.

The storageFiles enum has been renamed to StorageFiles to follow TypeScript’s PascalCase convention for type declarations.

import { storageFiles } from '@crowdin/app-project-module';
import { StorageFiles } from '@crowdin/app-project-module';

The integrationPagination option of the projectIntegration module has been removed. Page-based pagination of the integration files list (the fileListEnd event that re-requested files with an incremented page number) is no longer supported.

const configuration = {
projectIntegration: {
integrationPagination: true,
getIntegrationFiles: async ({ parentId, page }) => {
getIntegrationFiles: async ({ parentId, paginationData }) => {
// ...
},
},
};

Migration: switch to the Load More pagination pattern - return a load-more node with a pagination object, and read the cursor back from the paginationData parameter on the next call. The page parameter is still passed to getIntegrationFiles, but it is always 0.

The deprecated crowdinFilesWidget custom form widget has been removed together with the legacy Crowdin web components it was built on. Forms that reference it in formUiSchema now fall back to the default widget for the property type.

formUiSchema: {
files: {
"ui:widget": 'crowdinFilesWidget',
},
}

Migration: there is no drop-in replacement. Populate the property with the files you want to offer (for example via enum/ui:enumNames, or a select) and load them from the Crowdin API yourself, or move file selection into a project integration, which renders the files tree natively. The monacoEditorWidget and htmlWidget widgets are unaffected.

v2 is a major release that streamlines the module’s configuration and storage layers. Module declarations are now consistently array-based, custom database tables are powered by Drizzle ORM, and the package’s public import paths have been tidied up. A few legacy options - MySQL storage and the AI Tools modules - have been removed. The sections below cover each breaking change and the steps to migrate.

All module declarations now require array syntax. Single-object declarations are no longer supported for module properties except projectIntegration and api.

Before:

const configuration = {
customMT: {
translate
}
};

After:

const configuration = {
customMT: [
{
translate
}
]
};

Affected module properties:

  • customFileFormat, customMT, profileResourcesMenu, profileSettingsMenu, organizationMenu, organizationSettingsMenu
  • editorRightPanel, projectMenu, projectMenuCrowdsource, projectTools, projectReports, contextMenu, modal
  • filePreImport, filePostImport, filePreExport, filePostExport, fileTranslationsAlignmentExport
  • customSpellchecker, aiProvider, aiPromptProvider, aiRequestPreCompile, aiRequestPostCompile, aiRequestPreParse, aiRequestPostParse
  • externalQaCheck, webhooks, workflowStepType, automationAction, authGuard

Migration:

  1. Wrap each affected module configuration object in [...].
  2. Keep projectIntegration and api as single objects.
  3. If you configure multiple instances, keep using the same array with multiple items.

Custom Database Tables Now Use Drizzle ORM

Section titled “Custom Database Tables Now Use Drizzle ORM”

The module now uses Drizzle ORM for custom tables. The previous raw-SQL API - storage.registerCustomTable() and the related custom query helpers - has been removed.

Before:

await crowdinApp.storage.registerCustomTable('translations', {
id: 'INTEGER PRIMARY KEY AUTOINCREMENT',
content: 'TEXT',
});

After:

import * as schema from './schema.js';
configuration.customTables = {
schema,
migrationFolder: './migrations', // SQLite / PostgreSQL
d1Migrations: migrations, // Cloudflare D1
};
// access the Drizzle instance
const db = crowdinApp.storage.db;

Migration:

  1. Install drizzle-orm and drizzle-kit.
  2. Define your tables as a Drizzle schema and generate migrations (or declare them manually for D1).
  3. Register them via configuration.customTables and rewrite custom queries with the Drizzle API.

See the Database guide for full details.

Public import subpaths have been consolidated. Only documented paths are supported; internal paths may change without notice.

Removed:

  • @crowdin/app-project-module/out/* - internal build output, no longer exposed
  • @crowdin/app-project-module/storage
  • @crowdin/app-project-module/modules/integration/util

Added:

  • @crowdin/app-project-module/test - app testing utilities

Migration: import Storage and other types from the main entrypoint @crowdin/app-project-module, and replace any /out/* imports with the corresponding public subpath. See supported import paths.

The aiTools and aiToolsWidget modules have been removed. Remove these options from your configuration; there is no direct replacement.

MySQL is no longer a supported storage backend. The mysqlConfig option has been removed. Supported databases are now SQLite (default), PostgreSQL, and Cloudflare D1.

Migration: switch to PostgreSQL (or SQLite). There is no automatic MySQL migration - move your data to a supported database manually.

Crowdin app helpers that previously lived in the standalone npm package @crowdin/crowdin-apps-functions are now shipped with this module. Prefer the built-in entry points so your app stays aligned with the Apps SDK version you already depend on.

Migration from @crowdin/crowdin-apps-functions

Section titled “Migration from @crowdin/crowdin-apps-functions”
  • Action required: Remove @crowdin/crowdin-apps-functions from your dependencies and replace imports with the subpaths below.
  • Token helpers (OAuth, app tokens, JWT validation, Crowdin id parsing) - import from @crowdin/app-project-module/functions/token.
  • Crowdin API helpers (files, folders, translations, webhooks, subscriptions) - import from @crowdin/app-project-module/functions/crowdin.

The API surface matches what the standalone library provided; see App Functions for exports, subpath names, and usage examples.

The project now uses react-jsonschema-form v6. Modules that define UI as a formSchema should review the breaking changes below.

  • enumNames - Use ui:enumNames in UiSchema instead of enumNames on the schema
  • idSchema, formContext, classNames, themes, callbacks - see the upgrade guide for details

See the 6.x Upgrade Guide for full details.

Major release introducing Cloudflare Workers support, Express 5 upgrade, and architectural improvements.

  • Action required: Test your application thoroughly after upgrading
  • Potential issues: Some third-party middleware may need updates, subtle error handling differences
  • Action required: If using S3 file processing, manually install:
    Terminal window
    npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
  • Benefit: Smaller bundle size for apps not using AWS S3

All functions now follow the parameter object pattern (also known as an options object or named parameters via destructuring).

This means functions that previously took several positional arguments now accept a single options object - destructured in the signature - which improves readability and makes optional parameters easier to manage.

Before:

// positional parameters
getCrowdinFiles: (projectId: number, client: Crowdin, appRootFolder?: SourceFilesModel.Directory, config?: any, mode?: CrowdinFilesLoadMode) => Promise<TreeItem[]>;

After:

// parameter object (destructured)
getCrowdinFiles: (options: {
projectId: number;
client: Crowdin;
rootFolder?: SourceFilesModel.Directory;
settings?: any;
mode?: CrowdinFilesLoadMode;
}) => Promise<TreeItem[]>;

Example:

Before:

configuration.projectIntegration.cronJobs = [
{
expression: '*/10 * * * * *',
task: (projectId, credentials, rootFolder, settings) => {
console.log(`Running background task for project: ${projectId}`);
console.log(`Api credentials: ${JSON.stringify(credentials)}`);
console.log(`App config: ${JSON.stringify(settings)}`);
console.log(rootFolder ? JSON.stringify(rootFolder) : 'No root folder');
}
}
];

After:

configuration.projectIntegration.cronJobs = [
{
expression: '*/10 * * * * *',
task: ({ projectId, credentials, rootFolder, settings } = {}) => {
console.log(`Running background task for project: ${projectId}`);
console.log(`Api credentials: ${JSON.stringify(credentials)}`);
console.log(`App config: ${JSON.stringify(settings)}`);
console.log(rootFolder ? JSON.stringify(rootFolder) : 'No root folder');
}
}
];

List of all affected functions (some functions that already used parameter objects were renamed or had parameter names updated):

Updated functions (now accept a parameter object)

Section titled “Updated functions (now accept a parameter object)”

These functions now accept a single destructured options object (e.g., fn({ a, b, c })).

  • decryptCrowdinConnection
  • establishCrowdinConnection
  • log
  • onError
  • onUninstall
  • saveMetadata
  • aiPromptProvider
    • compile
  • aiProvider
    • chatCompletions
  • aiRequestPreCompile
    • processRequest
    • processStream
  • aiTools
    • toolCalls
  • automationAction
    • execute
    • validateSettings
  • customMT
    • translate
File processing (modules: customFileFormat, filePreImport, filePostImport, filePreExport, filePostExport, fileTranslationsAlignmentExport)
Section titled “File processing (modules: customFileFormat, filePreImport, filePostImport, filePreExport, filePostExport, fileTranslationsAlignmentExport)”
  • buildFile
  • exportStrings
  • fileProcess
  • parseFile
  • crowdinWebhookInterceptor
  • crowdinWebhooks
  • getAuthorizationUrl
  • getConfiguration
  • getCrowdinFiles
  • getFileProgress
  • getIntegrationFiles
  • integrationWebhookInterceptor
  • integrationWebhooks
  • matchCrowdinFilesToIntegrationFiles
  • normalizeSettings
  • onLogout
  • performGetTokenRequest (oauthLogin)
  • performRefreshTokenRequest (oauthLogin)
  • task
  • updateCrowdin
  • updateIntegration
  • validateSettings
  • D1 Database: d1Config for serverless SQL database
  • Custom Cron: cron option for Workers scheduled events
  • File Storage: fileStore interface for external storage (S3, R2, etc.)
  • Assets: assetsConfig for Workers Assets integration, assetsPath for custom static directory
  • Webhooks: deferResponse: true required for proper Workers execution
  1. Update package.json: "@crowdin/app-project-module": "^1.0.0"
  2. Install AWS SDK if needed: (see breaking changes above)
  3. Test Express 5 compatibility
  • Updated: Express 4 → 5, React 18 added (internal)
  • Moved to optional: AWS SDK packages