Directory Config
Page size, sort options, filters, and seed categories.
File: config/directory.config.ts
Controls how your directory listing behaves — how many items per page, how users can sort them, which categories are pre-seeded on first install.
Fields
| Field | Type | What it does |
|---|---|---|
pageSize | number | Projects per page. Default: 40. |
sortOptions | array | Each option has value, label, and a DB sort object. |
defaultSort | string | Must match a value from sortOptions. |
pricingOptions | array | Filter options for the pricing filter UI. |
seedCategories | array | Categories auto-created on first setup if the table is empty. |
Sort options
Each sort option maps a human label to a database sort clause:
sortOptions: [
{ value: 'newest', label: 'Latest', sort: { created_at: -1, premium_badge: -1 } },
{ value: 'popular', label: 'Most Popular', sort: { upvotes: -1, premium_badge: -1, created_at: -1 } },
{ value: 'top_rated', label: 'Top Rated', sort: { average_rating: -1, ratings_count: -1 } },
{ value: 'views', label: 'Most Views', sort: { views: -1, premium_badge: -1, upvotes: -1 } },
{ value: 'name_asc', label: 'Name (A to Z)', sort: { name: 1, created_at: -1 } },
{ value: 'name_desc', label: 'Name (Z to A)', sort: { name: -1, created_at: -1 } },
],-1 = descending, 1 = ascending. The sort is applied in order: first key, then tie-breakers.
To add a new sort, add the object and ensure the field exists in the apps table.
Pricing filter
pricingOptions: [
{ value: 'all', label: 'All' },
{ value: 'free', label: 'Free' },
{ value: 'freemium', label: 'Freemium' },
{ value: 'paid', label: 'Paid' },
],The value is used both in URL params and for DB filtering. To add a new price tier, add the option and add the matching value to the apps.pricing_type enum in the schema.
Seed categories
These are inserted into the categories table on first run if it's empty:
seedCategories: [
{ name: 'SaaS', slug: 'saas', sphere: 'Software', sort_order: 1, description: '...' },
{ name: 'Developer Tools', slug: 'developer-tools', sphere: 'Software', sort_order: 2, description: '...' },
{ name: 'Design', slug: 'design', sphere: 'Creative', sort_order: 3, description: '...' },
{ name: 'Marketing', slug: 'marketing', sphere: 'Business', sort_order: 4, description: '...' },
{ name: 'Other', slug: 'other', sphere: 'Other', sort_order: 99, description: '...' },
],Once there's at least one category in the DB, this array is ignored. Manage categories through /admin/categories after that.
Replace these with categories matching your niche before your first deploy, so they seed correctly.
Replace the
seedCategoriesarray in config/directory.config.ts with {5-8} categories tailored to my directory about {your niche, e.g. "AI writing tools" or "Buenos Aires coworking spaces"}.For each category provide:
- name (display)
- slug (url-safe, lowercase, hyphenated)
- description (1 sentence)
- sphere (a parent grouping — reuse the same sphere across related categories)
- sort_order (1, 2, 3, …; last one gets 99 if it's a catch-all)
End with a "catch-all" category named "Other" (slug: "other", sphere: "Other", sort_order: 99) so submissions that don't fit elsewhere have a home.
Preserve the DirectoryConfig type and all other fields (pageSize, sortOptions, pricingOptions).
Helpers exported
import { getSortFields, getPricingValues, getSortValues } from '@/config/directory.config';
getSortFields('popular'); // → { upvotes: -1, premium_badge: -1, created_at: -1 }
getPricingValues(); // → ['all', 'free', 'freemium', 'paid']
getSortValues(); // → ['newest', 'popular', ...]These are used by API routes for validation and sort resolution.