Skip to main content

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

FieldTypeWhat it does
pageSizenumberProjects per page. Default: 40.
sortOptionsarrayEach option has value, label, and a DB sort object.
defaultSortstringMust match a value from sortOptions.
pricingOptionsarrayFilter options for the pricing filter UI.
seedCategoriesarrayCategories 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: '...' },
],
Seed is only applied when the table is empty

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.

AI Prompt· Generate seed categories for my niche

Replace the seedCategories array 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.