Skip to main content
interlace
Plugin: conventionsRules

no-deprecated-api

Prevent usage of deprecated APIs with migration context and timeline. This rule is part of eslint-plugin-conventions and provides LLM-optimized error messag

Prevent usage of deprecated APIs with migration context and timeline. This rule is part of eslint-plugin-conventions and provides LLM-optimized error messages with fix suggestions.

💡 Provides suggestions

Quick Summary

AspectDetails
CWE ReferenceCWE-1078 (Deprecated Components)
SeverityWarning (maintenance best practice)
Auto-Fix💡 Suggestion (renames to the replacement API)
CategoryCode Maintenance
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForProjects using libraries with deprecation timelines

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

⚠️ [CWE-1078](https://cwe.mitre.org/data/definitions/1078.html) OWASP:A03 CVSS:5.3 | Deprecated API detected | MEDIUM
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A03_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-1078 OWASP:A03 CVSS:5.3
Issue DescriptionSpecific vulnerabilityDeprecated API detected
Severity & ComplianceImpact assessmentMEDIUM
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Rule Details

Reports every use of an API you list in apis, so a migration finishes instead of stalling. The configured names are matched as given:

  • a call of the name (oldFetch(url)), also under a local name it was imported as (import { oldFetch as fetchIt } from 'lib'; fetchIt(url));
  • constructing it (new OldClient());
  • a member of that name on any object (api.oldFetch, api['oldFetch']).

The suggestion renames the use to replacement; it is not offered for a renamed import, where the import itself has to change. Nothing is reported while apis is empty (the default).

Configuration

OptionTypeDefaultDescription
apisDeprecatedAPI[][]List of deprecated APIs
warnDaysBeforeRemovalnumber90Accepted for compatibility; has no effect: a listed API is reported whatever its removal date

DeprecatedAPI Object

PropertyTypeRequiredDescription
namestringYesDeprecated API name
replacementstringYesReplacement API
deprecatedSincestringYesISO date when deprecated
removalDatestringNoISO date when will be removed
reasonstringYesWhy it's deprecated
migrationGuidestringNoURL to migration guide

Examples

❌ Incorrect

With apis: [{ name: 'oldFetch', replacement: 'newFetch', … }, { name: 'OldClient', replacement: 'Client', … }]:

import { oldFetch, OldClient } from 'my-library';
oldFetch(url);
new OldClient(url);
api.oldFetch(url);

✅ Correct

// Using replacement API
import { newFunction } from 'my-library';

newFunction({ data: 'test' });

Configuration Examples

Library API Deprecation

{
  rules: {
    'conventions/no-deprecated-api': ['error', {
      warnDaysBeforeRemoval: 90,
      apis: [
        {
          name: 'oldFetch',
          replacement: 'newFetch',
          deprecatedSince: '2024-01-01',
          removalDate: '2024-12-31',
          reason: 'Security improvements and better error handling',
          migrationGuide: 'https://docs.example.com/migration/fetch'
        },
        {
          name: 'legacyAuth',
          replacement: 'modernAuth',
          deprecatedSince: '2024-06-01',
          removalDate: '2025-06-01',
          reason: 'OAuth 2.0 compliance',
          migrationGuide: 'https://docs.example.com/migration/auth'
        }
      ]
    }]
  }
}

Internal API Sunset

{
  rules: {
    'conventions/no-deprecated-api': ['warn', {
      warnDaysBeforeRemoval: 30,
      apis: [
        {
          name: 'UserService',
          replacement: 'CustomerService',
          deprecatedSince: '2024-10-01',
          removalDate: '2024-12-31',
          reason: 'Renamed to match ubiquitous language',
          migrationGuide: 'https://wiki.internal.com/customer-service-migration'
        }
      ]
    }]
  }
}

Why This Matters

IssueImpactSolution
⏰ Breaking ChangesUnexpected runtime failuresEarly warnings
📚 DocumentationDevelopers unaware of deprecationsClear migration guides
🔄 Tech DebtOld code accumulatesProactive migration
🛡️ SecurityUsing insecure legacy APIsEnforce modern alternatives

Comparison with Alternatives

Featureno-deprecated-apieslint-plugin-deprecationTypeScript deprecation
Custom Deprecations✅ Yes⚠️ Limited⚠️ JSDoc only
Timeline Support✅ Yes❌ No❌ No
Suggestions✅ Yes❌ No❌ No
LLM-Optimized✅ Yes❌ No❌ No
ESLint MCP✅ Optimized❌ No❌ No
Migration Guides✅ Yes❌ No❌ No

Further Reading

Known False Negatives

  • A reference that is not called or constructed (const f = oldFetch;, class X extends OldClient {}) is not reported where it is written.

Known False Positives

  • Names are matched without their module: an API of the same name from a different library is reported too.

Use this rule

npm install -D oxlint-plugin-conventions
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-conventions"],  "rules": {    "conventions/no-deprecated-api": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-conventions'],    rules: {      'conventions/no-deprecated-api': 'error',    },  },});

oxlint-plugin-conventions re-exports eslint-plugin-conventions/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.