Upgrade Guide
This guide provides step-by-step instructions for upgrading existing StruxJS projects to newer framework versions, covering dependency management, version-specific changes, and post-upgrade verification.
General Upgrade Workflow
Upgrading a StruxJS application consists of updating your core dependencies, clearing compiled artifacts, recompiling TypeScript, and running tests.
1. Check Current Version
Inspect the version of struxjs-core currently installed in your project:
npm list struxjs
# or
npm list struxjs-core2. Update Core Framework
StruxJS projects created via create-strux-app alias struxjs-core to struxjs in package.json:
"dependencies": {
"struxjs": "npm:struxjs-core@^1.0.12"
}To update to the latest release, run the appropriate command for your package manager:
# If using the default alias 'struxjs':
npm install struxjs@npm:struxjs-core@latest
# Or if you installed 'struxjs-core' directly:
npm install struxjs-core@latest# If using the default alias 'struxjs':
pnpm add struxjs@npm:struxjs-core@latest
# Or if you installed 'struxjs-core' directly:
pnpm add struxjs-core@latest# If using the default alias 'struxjs':
yarn add struxjs@npm:struxjs-core@latest
# Or if you installed 'struxjs-core' directly:
yarn add struxjs-core@latestAlternatively, you can manually update the version number in your package.json and run npm install:
{
"dependencies": {
"struxjs": "npm:struxjs-core@^1.0.12"
}
}3. Clear Cache and Rebuild
After updating packages, clear any previous compiled artifacts in dist/ and recompile TypeScript:
# Remove build artifacts
rm -rf dist
# Recompile TypeScript
npm run build4. Verify & Test
Run your test suite and verify the application boots properly:
# Run tests (if configured)
npm test
# Start in development mode
npm run devUpgrading to v1.0.12 (from v1.0.11)
Highlights
- Zero Breaking Changes: Fully backward compatible with
1.0.11. - IoC Container Resolution for Default, Optional & Rest Parameters: Resolved an issue where registering or resolving classes with default arguments (such as
Route.middleware(ApiAuthMiddleware)orRoute.middleware(AuthMiddleware)) threw[StruxJS IoC Error]: Auto-injection failed for parameter 'defaultGuard="api"'. The IoC container now properly detects parameters with default values (= defaultValue), optional modifiers (?), and rest parameters (...args), applying default parameter values automatically when no explicit container binding exists. Auth.guard(name)Facade &AuthGuardInterface:Auth.guard('web')is now officially exposed with TypeScript type definitions, returning the correspondingAuthGuarddriver for multi-guard scoping.- Dedicated Web & API Separation for Middlewares: Simplified
AuthMiddlewareto focus purely on web session authentication and redirect handling, whileApiAuthMiddlewarehandles stateless JWT verification and401 Unauthorizedresponses.
Upgrading to v1.0.11 (from v1.0.10)
Highlights
- Zero Breaking Changes: Fully backward compatible with
1.0.10. - Middleware Constructor Arguments & Fluent Helpers: All security middlewares (
CanMiddleware,RoleMiddleware,PermissionMiddleware,AuthMiddleware,ApiAuthMiddleware) can now be passed as instantiated objects (new CanMiddleware('edit-post')) or via convenient fluent helpers / static factories (can('edit-post'),role('admin'),permission('publish'),ApiAuthMiddleware.guard('admin')) without requiring container binding. - ORM Model Primary Key
idAccessor Fix: Resolved an issue where model instances queried from the database hadidevaluate toundefineddue to modern ES2022 class field initialization.BaseModelnow uses an ambientdeclare id: anydeclaration and Proxy-prioritized resolution forid,_id, and table primary keys.
Recommended Update for Existing Projects: ApiAuthMiddleware.ts
If your existing application has app/Middleware/ApiAuthMiddleware.ts, update it to support constructor guard arguments and attach the resolved user directly to the request context:
// app/Middleware/ApiAuthMiddleware.ts
import { Middleware, Request, Response, Auth } from "struxjs";
export class ApiAuthMiddleware implements Middleware {
constructor(private defaultGuard: string = "api") {}
public static guard(guardName: string): ApiAuthMiddleware {
return new ApiAuthMiddleware(guardName);
}
public async handle(request: Request, response: Response, guard?: string): Promise<void> {
const targetGuard = guard || this.defaultGuard;
if (!(await Auth.jwt().check(targetGuard))) {
response.status(401).send({ error: "Unauthorized" });
return;
}
const user = await Auth.jwt().user(targetGuard);
if (!user) {
response.status(401).send({ error: "Unauthorized" });
return;
}
request.setUser(user);
}
public toString(): string {
return this.defaultGuard !== "api" ? `apiauth:${this.defaultGuard}` : "ApiAuthMiddleware";
}
}Recommended Update for Existing Projects: AuthMiddleware.ts
If your application has app/Middleware/AuthMiddleware.ts, update it to support custom redirect paths and guard parameters:
// app/Middleware/AuthMiddleware.ts
import { Middleware, Request, Response, Auth } from "struxjs";
export class AuthMiddleware implements Middleware {
constructor(
private redirectTo: string = "/login",
private guard: string = "web"
) {}
public static redirectTo(url: string, guard: string = "web"): AuthMiddleware {
return new AuthMiddleware(url, guard);
}
public async handle(
request: Request,
response: Response,
redirectParam?: string,
guardParam?: string
): Promise<void> {
const targetRedirect = redirectParam || this.redirectTo;
const targetGuard = guardParam || this.guard;
if (await Auth.guard(targetGuard).guest()) {
response.redirect(targetRedirect);
}
}
public toString(): string {
return this.redirectTo !== "/login" ? `auth:${this.redirectTo}` : "AuthMiddleware";
}
}Upgrading to v1.0.10 (from v1.0.9)
Dual-Guard Authorization (JWT & Session Support)
Prior to 1.0.10, authorization checks (Gate.allows(), Gate.authorize(), CanMiddleware, RoleMiddleware) strictly inspected the Web Session cookie (_auth_id), which meant JWT-authenticated requests (Authorization: Bearer <token>) were evaluated with user = null and resulted in unexpected 403 Forbidden responses unless Gate.forUser(user) was called manually.
In 1.0.10:
- Unified Dual-Guard Resolution:
Gate.allows(),Gate.denies(), andGate.authorize()automatically resolve the authenticated user from either Session cookies or JWT Bearer tokens. - Route Middlewares on API Routes:
RoleMiddleware(role:admin,editor) andCanMiddleware(can:ability) now seamlessly inspect JWT Bearer tokens on API routes. - New
PermissionMiddleware: Direct permission route middleware (permission:publish-post). - Flexible
HasRoles: Safely extracts roles and permissions fromuser.role(string),user.roles(array or JSON-encoded database string), and modelattributes.
Upgrading to v1.0.9 (from v1.0.8)
High Impact Highlights
- Zero Breaking Changes: Fully backward compatible with
1.0.8. - IoC Container Resolution Caching: Constructor dependency plans are now cached using a
WeakMap. Repetitive reflection and regex parameter extraction during class resolution are eliminated. - Router Action Parameter Caching: Controller action parameter resolvers are pre-compiled and cached on first invocation, significantly reducing routing overhead on subsequent HTTP requests.
Optional: Adding ESLint to Existing Projects
Starting in 1.0.9, the official StruxJS project template includes ESLint (Flat Config) configured for TypeScript. If your existing project does not yet have ESLint configured, you can add it as follows:
1. Install ESLint dependencies
npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin2. Create eslint.config.js in your project root
import tsPlugin from "@typescript-eslint/eslint-plugin";
import tsParser from "@typescript-eslint/parser";
export default [
{
ignores: [
"dist/**",
"node_modules/**",
"public/**",
"resources/**",
"storage/**",
],
},
{
files: ["app/**/*.ts", "config/**/*.ts", "routes/**/*.ts", "database/**/*.ts", "bootstrap*.ts", "*.ts"],
languageOptions: {
parser: tsParser,
ecmaVersion: "latest",
sourceType: "module",
},
plugins: {
"@typescript-eslint": tsPlugin,
},
rules: {
...tsPlugin.configs.recommended.rules,
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": ["warn", { "args": "none", "varsIgnorePattern": "^_" }],
"no-console": "off",
"no-empty": ["error", { "allowEmptyCatch": true }],
},
},
];3. Add lint scripts to package.json
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}Upgrading to v1.0.8 (from v1.0.7)
Authentication & Token Security Updates
1. Strict Token Type Separation
In 1.0.8, access tokens are signed with type: "access", and refresh tokens are signed with type: "refresh".
- The
ApiAuthMiddlewareandJwtGuard.resolveRequestPayload()automatically reject requests if a client attempts to pass a Refresh Token to an endpoint expecting an Access Token. - If your frontend previously reused refresh tokens in
Authorization: Bearer <token>headers on regular API routes, ensure your frontend client correctly routes the access token for standard API requests and reserves the refresh token exclusively for token refresh endpoints (/auth/refresh).
2. Shared JTI and Token Rotation
When calling JwtGuard.issueTokenPair(user):
- Both access and refresh tokens share the same
jtiidentifier. - When
refreshToken(oldRefreshToken, { rotation: true })is invoked, the previousjtiis blacklisted. This immediately revokes the associated access token as well, mitigating replay attack risks.
3. Redis TTL String Parsing
TTL configuration values in .env or configurations can now safely use string notation:
- Values such as
JWT_REFRESH_TTL=30d,JWT_EXPIRES_IN=1h,"15m", or"60s"are automatically parsed to positive integers byparseTtlToSeconds(), preventing Redis errors likeERR value is not an integer or out of range.
4. Global Redis Prefix
The JWT blacklist and refresh token stores now respect REDIS_PREFIX defined in .env (e.g. REDIS_PREFIX=strux_), keeping Redis keys organized and collision-free across multiple environments.
Troubleshooting & FAQs
Package resolution issues
If your package manager fails to resolve the aliased package:
npm cache clean --force
rm -rf node_modules package-lock.json
npm installTypeScript build errors after upgrade
Ensure your dist/ directory is deleted before compiling, as outdated .d.ts or .js files might conflict:
rm -rf dist
npm run buildVerifying Environment Variables
Whenever upgrading between minor or major versions, check if new configuration options are available in .env.example of the latest StruxJS template (e.g., Redis prefixes or JWT TTL formats).