Yext Categories Service
๐ Overviewโ
The Yext Categories service retrieves available business categories from Yext's category taxonomy. Categories are used to classify business entities (locations) and determine which publisher directories are appropriate for listing distribution. Proper category selection ensures listings appear in relevant directories.
Source File: external/Integrations/Yext/services/categories.service.js
External API: Yext Categories API
Primary Use: List available business categories for entity classification
๐๏ธ Collections Usedโ
accountโ
- Operations: Read (via account service)
- Model:
external/models/account.js - Usage Context: Verify account has entity initialized before API call
๐ Data Flowโ
sequenceDiagram
participant Client
participant CategoriesService
participant AccountService
participant AccountDB
participant YextAPI
Client->>CategoriesService: list(accountId, query)
CategoriesService->>AccountService: find(accountId)
AccountService->>AccountDB: Check entity initialization
alt Entity initialized
AccountDB-->>AccountService: Return entity ID
AccountService-->>CategoriesService: Entity ID
CategoriesService->>YextAPI: GET /categories
YextAPI-->>CategoriesService: Categories list
CategoriesService-->>Client: Return categories
else Not initialized
AccountDB-->>AccountService: Error object
AccountService-->>CategoriesService: Error
CategoriesService-->>Client: 404 Not Found
end
style CategoriesService fill:#e3f2fd
style YextAPI fill:#fff4e6
๐ง Business Logic & Functionsโ
list({ query, accountId })โ
Purpose: Retrieve list of available business categories from Yext
Source: services/categories.service.js
External API Endpoint: GET https://api.yext.com/v2/categories
Parameters:
query(Object) - Query parameterslanguage(String, optional) - Language code (e.g., 'en', 'es')country(String, optional) - Country code (e.g., 'US', 'CA')
accountId(ObjectId) - DashClicks account ID
Returns: Promise<Object> - Categories data from Yext
Business Logic Flow:
-
Verify Account Initialization
const account = await accountService.find(accountId);
if (account?.error) {
throw notFound('Record not found.');
} -
Fetch Categories from Yext
const v = process.env.YEXT_API_VPARAM || '20200525';
const url = 'https://api.yext.com/v2/categories?' + querystring.encode({ v: v });
const options = {
method: 'GET',
headers: { 'api-key': process.env.YEXT_API_KEYS },
params: query,
url,
};
const dns = await axios(options);
return dns.data;
API Request Example:
GET https://api.yext.com/v2/categories?v=20200525&language=en&country=US
Headers:
api-key: {YEXT_API_KEY}
API Response Example:
{
"meta": {
"uuid": "abc-def-123",
"errors": []
},
"response": {
"categories": [
{
"id": "8040",
"name": "Restaurant",
"fullName": "Food & Beverage > Restaurant",
"parent": "1000",
"shortName": "Restaurant"
},
{
"id": "1234",
"name": "Coffee Shop",
"fullName": "Food & Beverage > Coffee & Tea > Coffee Shop",
"parent": "8040",
"shortName": "Coffee Shop"
},
{
"id": "5678",
"name": "Plumber",
"fullName": "Home Services > Plumbing > Plumber",
"parent": "5000",
"shortName": "Plumber"
},
{
"id": "9101",
"name": "Law Firm",
"fullName": "Professional Services > Legal > Law Firm",
"parent": "9000",
"shortName": "Law Firm"
}
// ... hundreds more categories
]
}
}
Category Structure:
id- Unique category identifier (used in entity creation)name- Display namefullName- Full hierarchical pathparent- Parent category ID (for hierarchical navigation)shortName- Abbreviated name
Error Handling:
- Account Not Initialized: Throws 404 Not Found with "Record not found."
- API Errors: Yext API errors propagate up
Example Usage:
// Get all categories
const categories = await categoriesService.list({
accountId: '507f1f77bcf86cd799439011',
query: {
language: 'en',
country: 'US',
v: '20200525',
},
});
console.log('Total categories:', categories.response.categories.length);
// Find restaurant categories
const restaurants = categories.response.categories.filter(cat =>
cat.fullName.includes('Restaurant'),
);
console.log('Restaurant categories:', restaurants);
Use in Entity Creation:
// 1. Get categories
const categories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});
// 2. Find appropriate category
const coffeeCat = categories.response.categories.find((cat) => cat.name === 'Coffee Shop');
// 3. Create entity with category
const entity = await entitiesService.create({
accountId: accountId,
body: {
name: 'My Coffee Shop',
categoryIds: [coffeeCat.id], // Use category ID
address: {...},
phone: '...'
},
query: { v: '20200525' }
});
Query Parameters:
| Parameter | Type | Description | Required |
|---|---|---|---|
language | String | Language code (e.g., 'en', 'es', 'fr') | โ |
country | String | Country code (e.g., 'US', 'CA', 'GB') | โ |
v | String | API version (default: from env variable) | โ |
Key Business Rules:
- Account Verification: Requires account to have entity initialized
- Hierarchical Structure: Categories organized in parent-child hierarchy
- Localization: Supports multiple languages and countries
- Static Data: Categories rarely change, suitable for caching
- Multiple Categories: Entities can have multiple category IDs
๐ Integration Pointsโ
Entity Creation Workflowโ
// Complete workflow: Get categories โ Select category โ Create entity
async function createBusinessListing(accountId, businessData) {
// 1. Get available categories
const categoriesResponse = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});
// 2. Find appropriate category (e.g., by industry)
const category = categoriesResponse.response.categories.find(cat => {
return cat.name.toLowerCase().includes(businessData.industry.toLowerCase());
});
if (!category) {
throw new Error('No matching category found for industry: ' + businessData.industry);
}
// 3. Create entity with category
const entity = await entitiesService.create({
accountId: accountId,
body: {
name: businessData.name,
categoryIds: [category.id],
address: businessData.address,
phone: businessData.phone,
websiteUrl: businessData.website,
},
query: { v: '20200525' },
});
return {
entity: entity,
category: category,
};
}
Category Search Helperโ
// Helper function to search categories
function searchCategories(categories, searchTerm) {
const term = searchTerm.toLowerCase();
return categories.filter(cat => {
return (
cat.name.toLowerCase().includes(term) ||
cat.fullName.toLowerCase().includes(term) ||
cat.shortName.toLowerCase().includes(term)
);
});
}
// Usage
const categories = await categoriesService.list({
accountId: accountId,
query: { v: '20200525' },
});
const plumberCategories = searchCategories(categories.response.categories, 'plumber');
console.log('Plumber categories:', plumberCategories);
Category Hierarchy Builderโ
// Build category tree from flat list
function buildCategoryTree(categories) {
const categoryMap = {};
const roots = [];
// First pass: create map
categories.forEach(cat => {
categoryMap[cat.id] = { ...cat, children: [] };
});
// Second pass: build tree
categories.forEach(cat => {
if (cat.parent && categoryMap[cat.parent]) {
categoryMap[cat.parent].children.push(categoryMap[cat.id]);
} else {
roots.push(categoryMap[cat.id]);
}
});
return roots;
}
// Usage
const categories = await categoriesService.list({
accountId: accountId,
query: { v: '20200525' },
});
const tree = buildCategoryTree(categories.response.categories);
console.log('Category tree:', JSON.stringify(tree, null, 2));
๐งช Edge Cases & Special Handlingโ
Account Not Initializedโ
Issue: Account doesn't have Yext entity
Handling: Throws 404 Not Found
const account = await accountService.find(accountId);
if (account?.error) {
throw notFound('Record not found.');
}
Solution: Create entity first using entitiesService.create()
Large Category Listโ
Issue: Yext returns hundreds of categories (can be 500+)
Handling: No pagination, returns all categories
Recommendation: Cache categories in application or client-side
Example Caching:
let categoriesCache = null;
let cacheTimestamp = null;
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
async function getCategoriesWithCache(accountId) {
const now = Date.now();
if (categoriesCache && cacheTimestamp && now - cacheTimestamp < CACHE_TTL) {
return categoriesCache;
}
const categories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});
categoriesCache = categories;
cacheTimestamp = now;
return categories;
}
Missing Parent Categoriesโ
Issue: Category references parent that doesn't exist in list
Handling: No validation, parent relationship may be broken
Prevention: Use fullName for display instead of building tree
Localizationโ
Issue: Category names vary by language/country
Handling: Pass appropriate language and country codes
Example:
// English categories
const enCategories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});
// Spanish categories
const esCategories = await categoriesService.list({
accountId: accountId,
query: { language: 'es', country: 'US', v: '20200525' },
});
โ ๏ธ Important Notesโ
- Read-Only: This service only retrieves categories, doesn't create/modify
- Account Required: Must have entity initialized even though not used
- Static Data: Categories rarely change, suitable for aggressive caching
- Hierarchical: Categories have parent-child relationships via
parentfield - Use fullName:
fullNameprovides complete category path - Multiple Languages: Support for localized category names
- No Pagination: Returns all categories in single response
- Large Response: Can return 500+ categories
- Category IDs: Use
idfield when creating entities - Search: Consider implementing client-side search for better UX
๐ Related Documentationโ
- Yext Integration Overview: index.md
- Entities Service: entities.md - Uses category IDs in entity creation
- Account Service: account.md - Verifies account initialization
- Yext Categories API: https://developer.yext.com/docs/api-reference/categories/
๐ฏ Common Use Casesโ
1. Populate Category Dropdownโ
// Get categories for UI dropdown
const categories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});
// Format for dropdown
const options = categories.response.categories.map(cat => ({
value: cat.id,
label: cat.fullName,
}));
2. Category Autocompleteโ
// Search categories as user types
function autocompleteSuggest Categories(categories, input) {
const term = input.toLowerCase();
return categories
.filter((cat) => cat.name.toLowerCase().includes(term))
.slice(0, 10) // Limit to 10 suggestions
.map((cat) => ({
id: cat.id,
name: cat.name,
fullName: cat.fullName,
}));
}
3. Industry-Specific Categoriesโ
// Get categories for specific industries
const industries = {
restaurants: ['restaurant', 'cafe', 'coffee', 'bar', 'food'],
legal: ['law', 'attorney', 'legal'],
medical: ['doctor', 'dentist', 'medical', 'clinic', 'hospital'],
retail: ['store', 'shop', 'retail', 'boutique'],
};
function getCategoriesByIndustry(categories, industry) {
const keywords = industries[industry] || [];
return categories.filter(cat => {
return keywords.some(keyword => cat.fullName.toLowerCase().includes(keyword));
});
}