Equipment Fitment API Enhancement Task
Document Version: 1.0 Created: 2025-11-10 Status: Draft for Backend Team Review Related Frontend Work: Phase 4 - Equipment Fitment Integration
Equipment Fitment API Enhancement Task
Document Version: 1.0 Created: 2025-11-10 Status: Draft for Backend Team Review Related Frontend Work: Phase 4 - Equipment Fitment Integration
Overview
This document specifies the API enhancements needed to support interactive equipment fitment search, filtering, and sorting on product detail pages. Currently, equipment fitment data is displayed statically on the frontend. We need backend support for searching within fitment data, sorting by various criteria, filtering by category and year range, and paginating large result sets.
Current State
Existing Equipment-Related Endpoints and Parameters
The Search Service API (v1.3.0) already has foundational equipment support:
1. Equipment Filter Parameters (Search Endpoint)
Endpoint: GET/POST /api/search
Existing parameters:
equipmentFitment(string | string[]): Filter by equipment fitment stringshasEquipmentFitment(boolean): Filter to parts that have fitment dataequipmentModelKey(string | string[]): Filter by equipment model keys
2. Equipment Facets (Filters Endpoint)
Endpoint: GET /api/filters
Returns EquipmentFacet structure:
interface EquipmentFacet {
afterKey?: Record<string, unknown> | null;
buckets: EquipmentFacetBucket[];
}
interface EquipmentFacetBucket {
brandCode: string;
model: string;
docCount: number;
variants: number;
productLine?: string | null;
yearFrom?: number | null;
yearTo?: number | null;
}Pagination parameters:
equipmentAfterKey(string | object): Cursor for paginationequipmentLimit(number): Page size for equipment facetsequipmentPageSize(number): Alias for equipmentLimit
3. Part Response Structure
Endpoint: GET /api/parts/:id
Returns Part object with equipment fields:
equipmentFitment(string[] | null): Raw fitment strings (e.g., "T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY")equipmentModelKey(string[] | null): Equipment model keys for filteringequipmentKey(string[] | null): Additional equipment keysyearFrom(number[] | null): Year range start valuesyearTo(number[] | null): Year range end values
4. Pagination Infrastructure
The API already supports two pagination modes:
- Offset-based:
pageandpageSizeparameters - Cursor-based:
searchAfter,pitId,pitKeepAliveparameters
Requirements
Problem Statement
On product detail pages, parts can have 50-500+ equipment fitment records. Users need to:
- Search within fitment data (by model name, year, identifier)
- Filter by equipment category and year range
- Sort by different criteria (alphabetical, year, category)
- Paginate through large fitment lists
Current frontend implementation parses raw equipmentFitment strings client-side, which is inefficient and lacks search/filter capability.
Use Cases
- User searches for "T4040" within a part's fitment → API returns matching fitment records
- User filters by "TRACTORS UTILITY" category → API returns only that category's records
- User filters by year range 2010-2015 → API returns fitment records within that range
- User sorts by year descending → API returns newest equipment first
- User scrolls through 200+ fitment records → API paginates efficiently
API Specifications
Endpoint: Search Equipment Fitment for a Part
Method: GET
Path: /api/parts/:partId/equipment-fitment
Returns paginated, searchable, and sortable equipment fitment data for a specific part.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
partId | string | Yes | Part ID or slug |
Query Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | No | - | Search query (searches model name, identifier, raw string) |
category | string | No | - | Filter by exact category name (e.g., "TRACTORS UTILITY") |
yearFrom | number | No | - | Filter by minimum year (inclusive) |
yearTo | number | No | - | Filter by maximum year (inclusive) |
sortBy | enum | No | category | Sort criteria (see Sort Options below) |
sortOrder | enum | No | asc | Sort order: asc or desc |
page | number | No | 1 | Page number (1-based) |
pageSize | number | No | 50 | Items per page (max: 100) |
Sort Options
| Value | Description |
|---|---|
category | Sort by category name alphabetically |
model | Sort by model name alphabetically |
year | Sort by year range (uses yearFrom, then yearTo) |
relevance | Sort by search relevance (only when q is provided) |
Response Schema
interface EquipmentFitmentResponse {
// Grouped fitment data
fitmentGroups: EquipmentFitmentGroup[];
// Pagination metadata
pagination: {
page: number; // Current page (1-based)
pageSize: number; // Items per page
total: number; // Total items matching filters
totalPages: number; // Total pages
hasNextPage: boolean; // Whether there's a next page
hasPreviousPage: boolean; // Whether there's a previous page
};
// Search metadata
query?: string; // Echo back search query if provided
filters: {
category?: string; // Active category filter
yearFrom?: number; // Active year range filter
yearTo?: number; // Active year range filter
};
// Performance metrics
took: number; // Query execution time in ms
}
interface EquipmentFitmentGroup {
category: string; // Equipment category (e.g., "TRACTORS UTILITY")
items: EquipmentFitmentItem[];
itemCount: number; // Number of items in this group
}
interface EquipmentFitmentItem {
// Core data
raw: string; // Original fitment string from database
summary: string; // Parsed model name/description
category: string; // Equipment category
// Optional parsed fields
identifier?: string; // Model identifier/code (extracted from parentheses)
yearRange?: string; // Formatted year range (e.g., "12/07 → 12/14")
yearFrom?: number; // Parsed start year (for filtering/sorting)
yearTo?: number; // Parsed end year (for filtering/sorting)
// Search relevance (only when q is provided)
score?: number; // Search relevance score
}Success Response Example
{
"fitmentGroups": [
{
"category": "TRACTORS UTILITY",
"itemCount": 3,
"items": [
{
"raw": "T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY",
"summary": "T4040 DELUXE TRACTOR",
"category": "TRACTORS UTILITY",
"yearRange": "12/07 → 12/14",
"yearFrom": 2007,
"yearTo": 2014,
"identifier": null
},
{
"raw": "T4050 TRACTOR 01/09 12/15 (12345) - TRACTORS UTILITY",
"summary": "T4050 TRACTOR",
"category": "TRACTORS UTILITY",
"yearRange": "01/09 → 12/15",
"yearFrom": 2009,
"yearTo": 2015,
"identifier": "12345"
}
]
},
{
"category": "COMBINES",
"itemCount": 1,
"items": [
{
"raw": "CR9080 COMBINE 01/11 12/16 - COMBINES",
"summary": "CR9080 COMBINE",
"category": "COMBINES",
"yearRange": "01/11 → 12/16",
"yearFrom": 2011,
"yearTo": 2016,
"identifier": null
}
]
}
],
"pagination": {
"page": 1,
"pageSize": 50,
"total": 4,
"totalPages": 1,
"hasNextPage": false,
"hasPreviousPage": false
},
"filters": {},
"took": 12
}Error Responses
404 Not Found - Part doesn't exist
{
"success": false,
"error": "Part not found",
"code": "PART_NOT_FOUND"
}400 Bad Request - Invalid parameters
{
"success": false,
"error": "Invalid year range: yearFrom must be <= yearTo",
"code": "INVALID_PARAMETERS",
"issues": [
{
"code": "invalid_range",
"path": ["yearFrom"],
"message": "yearFrom must be less than or equal to yearTo"
}
]
}Endpoint: Get Available Equipment Categories
Method: GET
Path: /api/parts/:partId/equipment-fitment/categories
Returns list of unique equipment categories for a part (for filter dropdown UI).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
partId | string | Yes | Part ID or slug |
Response Schema
interface CategoriesResponse {
categories: CategoryBucket[];
total: number; // Total number of categories
took: number; // Query time in ms
}
interface CategoryBucket {
name: string; // Category name
count: number; // Number of fitment items in this category
}Success Response Example
{
"categories": [
{
"name": "TRACTORS UTILITY",
"count": 45
},
{
"name": "COMBINES",
"count": 12
},
{
"name": "BALERS",
"count": 8
}
],
"total": 3,
"took": 5
}Data Requirements
Indexing Requirements
For optimal performance, the following fields should be indexed:
-
Full-text search index on
equipmentFitmentarray field- Enables fast searching within fitment strings
- Should support partial matches and fuzzy search
-
Keyword index on parsed category field
- For exact category filtering
- Consider extracting category during indexing (parse " - CATEGORY" suffix)
-
Range index on year fields
- Parse MM/YY dates from fitment strings
- Index both
yearFromandyearTofor range queries - Format: "12/07" → 2007, "01/15" → 2015
Data Parsing Strategy
The backend should parse raw equipmentFitment strings during indexing:
Input format: "{MODEL} {MM/YY_START} {MM/YY_END} ({IDENTIFIER}) - {CATEGORY}"
Examples:
"T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY""CR9080 COMBINE 01/11 12/16 (12345) - COMBINES""SOME MODEL - OTHER EQUIPMENT"(no years or identifier)
Parsing rules:
- Category: Extract text after " - " separator (required)
- Identifier: Extract text within parentheses at end (optional)
- Years: Extract MM/YY patterns, take last 2 as start/end range (optional)
- Convert "12/07" → 2007
- Handle missing years gracefully
- Summary: Everything before " - " after removing years and identifier
Edge cases:
- Missing years (some old equipment)
- Missing identifier (most entries)
- Missing category (fallback to "Other equipment")
- Multiple parentheses (take last one as identifier)
Implementation Notes
Performance Considerations
-
Caching: Consider caching parsed fitment data per part
- Cache TTL: 24 hours (fitment data rarely changes)
- Cache key:
part:{partId}:equipment-fitment
-
Pagination: Use offset-based pagination for simplicity
- Max page size: 100 items
- For parts with 500+ fitment items, pagination is essential
-
Search:
- ElasticSearch/OpenSearch match query on
equipmentFitmentarray - Minimum 2 characters for search query
- Use fuzzy matching for typo tolerance
- ElasticSearch/OpenSearch match query on
-
Grouping:
- Group by category after filtering/searching
- Return groups sorted by sortBy parameter
- Within each group, items sorted by sortBy parameter
Database Considerations
Option A: Parse on Index
- Pros: Fast queries, no runtime parsing overhead
- Cons: Requires re-indexing existing data, larger index size
- Recommendation: Preferred for production
Add these fields to the ElasticSearch/OpenSearch document:
{
"equipmentFitment": ["T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY"],
"equipmentFitmentParsed": [
{
"raw": "T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY",
"summary": "T4040 DELUXE TRACTOR",
"category": "TRACTORS UTILITY",
"identifier": null,
"yearFrom": 2007,
"yearTo": 2014
}
]
}Option B: Parse on Query
- Pros: No re-indexing needed, smaller index
- Cons: Slower queries, parsing overhead on every request
- Recommendation: Acceptable for MVP, migrate to Option A later
Security Considerations
- Rate limiting: 100 requests/minute per IP
- Parameter validation:
- Sanitize
qparameter (max 100 chars) - Validate year range (1900-2100)
- Validate pageSize (max 100)
- Sanitize
- Part existence check: Verify part exists before querying fitment
Backward Compatibility
This is a new endpoint, so no backward compatibility concerns. The existing /api/parts/:id endpoint continues to return raw equipmentFitment strings unchanged.
Priority Classification
P0 (Must Have) - MVP Requirements
- ✅
/api/parts/:partId/equipment-fitmentendpoint - ✅ Search parameter (
q) - ✅ Category filter parameter (
category) - ✅ Pagination (
page,pageSize) - ✅ Basic sorting (
sortBy: category | model | year) - ✅ Parsed fitment data structure (
category,summary,yearRange)
P1 (Should Have) - Post-MVP Enhancements
- ✅ Year range filtering (
yearFrom,yearTo) - ✅ Sort order parameter (
sortOrder: asc | desc) - ✅ Relevance sorting (when search query provided)
- ✅
/api/parts/:partId/equipment-fitment/categoriesendpoint - ✅ Search relevance scores in response
P2 (Nice to Have) - Future Enhancements
- 🔄 Fuzzy search with typo tolerance
- 🔄 Highlighting matched terms in response
- 🔄 Equipment brand/manufacturer filtering
- 🔄 Export fitment data (CSV/Excel format)
- 🔄 Bulk fitment updates API (admin)
- 🔄 Equipment model suggestions API (autocomplete)
Testing Requirements
Unit Tests
-
Parsing logic:
- Parse fitment strings correctly (with/without years, identifiers)
- Handle edge cases (missing category, malformed strings)
- Convert MM/YY to full year correctly
-
Query building:
- Build correct ElasticSearch query for search
- Build correct filters for category/year range
- Build correct sort clauses
Integration Tests
-
Endpoint behavior:
- Returns 404 for non-existent part
- Returns 400 for invalid parameters
- Returns 200 with correct data structure
- Pagination works correctly (page 1, 2, 3, etc.)
-
Search functionality:
- Search matches model names
- Search matches identifiers
- Search matches partial strings
- Search returns empty results gracefully
-
Filtering:
- Category filter works correctly
- Year range filter works correctly
- Combined filters work correctly (AND logic)
-
Sorting:
- Sort by category works (alphabetical)
- Sort by model works (alphabetical)
- Sort by year works (numerical, with null handling)
- Sort order (asc/desc) works correctly
Performance Tests
-
Load testing:
- Handle 100 concurrent requests
- Response time < 200ms for typical query (50 items)
- Response time < 500ms for large dataset (500+ items)
-
Edge cases:
- Part with 0 fitment items
- Part with 1000+ fitment items
- Search with no matches
- Invalid sort parameter
Timeline Estimate
| Phase | Tasks | Estimated Time |
|---|---|---|
| Phase 1: Setup | • Review requirements• Design database schema changes• Set up test fixtures | 2-3 days |
| Phase 2: Parsing | • Implement fitment string parser• Write parser unit tests• Validate against production data | 3-4 days |
| Phase 3: Indexing | • Update ElasticSearch mapping• Implement re-indexing script• Run re-indexing on staging | 2-3 days |
| Phase 4: API Implementation | • Implement fitment endpoint• Implement categories endpoint• Add parameter validation• Write integration tests | 4-5 days |
| Phase 5: Testing & QA | • Performance testing• Edge case testing• Security testing• Code review | 2-3 days |
| Phase 6: Deployment | • Deploy to staging• Frontend integration testing• Deploy to production• Monitor & iterate | 2-3 days |
Total Estimated Time: 15-21 working days (3-4 weeks)
Critical Path: Parsing logic → Re-indexing → API implementation → Frontend integration
Questions for Backend Team
Technical Questions
-
Indexing strategy: Should we parse fitment strings during indexing (Option A) or at query time (Option B)?
- Recommendation: Option A for production performance
-
Re-indexing: Can we run a re-indexing job on production with zero downtime?
- Expected volume: ~X parts with equipment fitment data
- Average fitment items per part: ~Y
-
Search technology: Are we using ElasticSearch or OpenSearch?
- Affects query syntax and available features
-
Caching layer: Do we have Redis or similar for caching parsed fitment data?
- Would significantly improve performance for frequently accessed parts
Data Questions
-
Fitment string format: Are there other format variations besides the documented pattern?
- Need sample data for edge cases
-
Data quality: What percentage of
equipmentFitmentstrings have:- Year information? (estimate: 80-90%)
- Identifier codes? (estimate: 30-40%)
- Valid category suffix? (estimate: 95%+)
-
Update frequency: How often does equipment fitment data change?
- Affects cache TTL strategy
Business Questions
-
Feature flag: Should this endpoint be behind a feature flag initially?
- Allows gradual rollout and A/B testing
-
Analytics: What metrics should we track?
- Search usage, popular categories, average result count, etc.
-
Rate limiting: What are appropriate rate limits for this endpoint?
- Suggestion: 100 req/min per IP, 1000 req/min per API key
TypeScript Type Definitions
For frontend integration, these types should be added to /lib/search-service/types.ts:
/**
* Equipment fitment item parsed from raw fitment string.
* Returned by /api/parts/:partId/equipment-fitment endpoint.
*/
export interface EquipmentFitmentItem {
/** Original fitment string from database */
raw: string;
/** Parsed model name/description (without years/identifier/category) */
summary: string;
/** Equipment category (e.g., "TRACTORS UTILITY", "COMBINES") */
category: string;
/** Model identifier/code extracted from parentheses (optional) */
identifier?: string | null;
/** Formatted year range (e.g., "12/07 → 12/14") for display */
yearRange?: string | null;
/** Parsed start year (numeric, for filtering/sorting) */
yearFrom?: number | null;
/** Parsed end year (numeric, for filtering/sorting) */
yearTo?: number | null;
/** Search relevance score (only present when q parameter provided) */
score?: number;
}
/**
* Group of equipment fitment items by category.
*/
export interface EquipmentFitmentGroup {
/** Category name */
category: string;
/** Fitment items in this category */
items: EquipmentFitmentItem[];
/** Number of items in this group */
itemCount: number;
}
/**
* Pagination metadata for equipment fitment results.
*/
export interface EquipmentFitmentPagination {
/** Current page number (1-based) */
page: number;
/** Items per page */
pageSize: number;
/** Total items matching filters */
total: number;
/** Total pages */
totalPages: number;
/** Whether there's a next page */
hasNextPage: boolean;
/** Whether there's a previous page */
hasPreviousPage: boolean;
}
/**
* Active filters for equipment fitment query.
*/
export interface EquipmentFitmentFilters {
/** Active category filter */
category?: string;
/** Active year range filter (minimum) */
yearFrom?: number;
/** Active year range filter (maximum) */
yearTo?: number;
}
/**
* Response from /api/parts/:partId/equipment-fitment endpoint.
*/
export interface EquipmentFitmentResponse {
/** Grouped fitment data by category */
fitmentGroups: EquipmentFitmentGroup[];
/** Pagination metadata */
pagination: EquipmentFitmentPagination;
/** Echo back search query if provided */
query?: string;
/** Active filters */
filters: EquipmentFitmentFilters;
/** Query execution time in milliseconds */
took: number;
}
/**
* Sort options for equipment fitment results.
*/
export type EquipmentFitmentSortBy = "category" | "model" | "year" | "relevance";
/**
* Sort order for equipment fitment results.
*/
export type EquipmentFitmentSortOrder = "asc" | "desc";
/**
* Parameters for equipment fitment search.
*/
export interface EquipmentFitmentParams {
/** Search query (searches model name, identifier, raw string) */
q?: string;
/** Filter by exact category name */
category?: string;
/** Filter by minimum year (inclusive) */
yearFrom?: number;
/** Filter by maximum year (inclusive) */
yearTo?: number;
/** Sort criteria */
sortBy?: EquipmentFitmentSortBy;
/** Sort order */
sortOrder?: EquipmentFitmentSortOrder;
/** Page number (1-based) */
page?: number;
/** Items per page (max: 100) */
pageSize?: number;
}
/**
* Category bucket for equipment fitment categories aggregation.
*/
export interface EquipmentCategoryBucket {
/** Category name */
name: string;
/** Number of fitment items in this category */
count: number;
}
/**
* Response from /api/parts/:partId/equipment-fitment/categories endpoint.
*/
export interface EquipmentCategoriesResponse {
/** List of categories with counts */
categories: EquipmentCategoryBucket[];
/** Total number of categories */
total: number;
/** Query execution time in milliseconds */
took: number;
}Example API Requests
Example 1: Basic Request (No Filters)
GET /api/parts/12345/equipment-fitment?page=1&pageSize=50Returns first 50 fitment items grouped by category, sorted by category name.
Example 2: Search Query
GET /api/parts/12345/equipment-fitment?q=T4040&page=1&pageSize=50Returns fitment items matching "T4040" search query, sorted by relevance.
Example 3: Category Filter
GET /api/parts/12345/equipment-fitment?category=TRACTORS+UTILITY&page=1&pageSize=50Returns only fitment items in "TRACTORS UTILITY" category.
Example 4: Year Range Filter
GET /api/parts/12345/equipment-fitment?yearFrom=2010&yearTo=2015&page=1&pageSize=50Returns fitment items with year range overlapping 2010-2015.
Example 5: Combined Filters with Sorting
GET /api/parts/12345/equipment-fitment?q=tractor&category=TRACTORS+UTILITY&yearFrom=2010&sortBy=year&sortOrder=desc&page=1&pageSize=50Returns tractors in "TRACTORS UTILITY" category from 2010 onwards, sorted by year descending.
Example 6: Get Categories
GET /api/parts/12345/equipment-fitment/categoriesReturns list of all categories available for this part.
Success Metrics
Performance Metrics
- Response time: < 200ms (p95) for typical query
- Search accuracy: > 95% relevant results for common queries
- Uptime: 99.9% availability
Usage Metrics
- Adoption: > 50% of product detail page visitors use search/filter
- Search success: > 80% of searches return results
- Category filter usage: Track most popular categories
Business Metrics
- Conversion impact: Measure impact on add-to-cart rate
- Support reduction: Fewer "compatibility" support tickets
- User satisfaction: Improved product detail page engagement time
Appendix: Sample Data
Sample Raw Fitment Strings
T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY
T4050 TRACTOR 01/09 12/15 (NH12345) - TRACTORS UTILITY
T4060 CAB TRACTOR 06/10 - TRACTORS UTILITY
CR9080 COMBINE 01/11 12/16 - COMBINES
CR9090 ELEVATION COMBINE 03/13 12/18 (CR9090ELEV) - COMBINES
BC5070 BALER 05/12 - BALERS
T5050 - TRACTORS COMPACT
LEGACY MODEL - OTHER EQUIPMENTSample Parsed Output
[
{
"raw": "T4040 DELUXE TRACTOR 12/07 12/14 - TRACTORS UTILITY",
"summary": "T4040 DELUXE TRACTOR",
"category": "TRACTORS UTILITY",
"identifier": null,
"yearRange": "12/07 → 12/14",
"yearFrom": 2007,
"yearTo": 2014
},
{
"raw": "T4050 TRACTOR 01/09 12/15 (NH12345) - TRACTORS UTILITY",
"summary": "T4050 TRACTOR",
"category": "TRACTORS UTILITY",
"identifier": "NH12345",
"yearRange": "01/09 → 12/15",
"yearFrom": 2009,
"yearTo": 2015
},
{
"raw": "T4060 CAB TRACTOR 06/10 - TRACTORS UTILITY",
"summary": "T4060 CAB TRACTOR",
"category": "TRACTORS UTILITY",
"identifier": null,
"yearRange": "06/10",
"yearFrom": 2010,
"yearTo": null
},
{
"raw": "LEGACY MODEL - OTHER EQUIPMENT",
"summary": "LEGACY MODEL",
"category": "OTHER EQUIPMENT",
"identifier": null,
"yearRange": null,
"yearFrom": null,
"yearTo": null
}
]Related Documentation
- Search Service API v1.3.0 Documentation
- Frontend Implementation Plan (coming soon)
- Equipment Fitment Data Schema (coming soon)
Document Status: Ready for backend team review Next Steps: Backend team to review and provide feedback on feasibility, timeline, and technical approach Contact: Frontend team lead for questions or clarifications