Setup
For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://buildwithfern.com/learn/llms.txt. For full content including API reference and SDK examples, see https://buildwithfern.com/learn/llms-full.txt.
Basic setup
Enable Ask Fern by adding the ai-search configuration to your docs.yml file:
After you enable Ask Fern, it needs to index your content before the side panel can appear on your site. This typically takes a few minutes, though sites with extensive custom components may take longer.
Configuration
ai-search.location
Specifies where Ask Fern will be available. Options:
docsenables Ask Fern on your documentation siteslackenables Ask Fern in Slackdiscordenables Ask Fern in Discord
Most users should enable Ask Fern for both docs and either slack (setup instructions) or discord (setup instructions).
ai-search.datasources
Additional content sources that Ask Fern should index and search. For more details, see Additional content sources.
datasources.url
The URL of the website to index. Ask Fern will crawl and index the content from this URL.
datasources.title
An optional display name for this datasource. This helps users understand where the information is coming from when Ask Fern cites content from this source.
ai-search.system-prompt
By default, Ask Fern uses system prompts to finetune AI search results. Add a custom prompt to override it.
See Anthropic’s prompting guide for ideas and examples.
ai-search.model
The AI model to use for Ask Fern responses. Options:
claude-3.7- Claude 3.7 Sonnet (default)claude-4- Claude 4 Sonnetcommand-a- Cohere Command A
If not specified, Ask Fern uses claude-3.7.
USE THIS:
What This App Is
CPS-APP is the official Convention Photography Services customer and admin app. It is a full-stack TypeScript monorepo with:
- An Express server (backend)
- A React/Vite PWA (frontend)
- Google Sheets as the only database
- No SQL, no Drizzle, no Postgres
Critical Rules
1. Do Not Add a Database
All order data lives in Google Sheets. The spreadsheet ID is GOOGLE_SPREADSHEET_ID. Do not add Drizzle, Postgres, SQLite, or any other SQL layer.
2. Photo and Event Data Comes from the PHP Backend
The PHP backend URL is PHP_BACKEND_URL (default: http://photos.conventionphotography.com). Events and photo thumbnails are fetched through these proxy routes:
GET /api/proxy-events→ lists eventsGET /api/proxy-image/:event/:photo→ serves a thumbnail
Do not query these services directly from the frontend — always go through the backend proxy.
3. Order Numbers Are Sequential Per Event
Order counters are stored in data/orderCounters.json as { "EVENTSLUG": 42 }. The order number format is EVENTSLUG-0042. This file must persist across server restarts (mount it as a volume in production).
4. The Frontend Uses Wouter, Not React Router
Routing is done with wouter. Do not install or use react-router-dom.
5. Data Fetching Uses TanStack Query v5
All HTTP data fetching in the frontend must use useQuery / useMutation from @tanstack/react-query. Use the object form only (v5 API). The default queryFn is wired up in client/src/lib/queryClient.ts to use the URL in queryKey[0].
6. Styling Uses Tailwind + shadcn
All UI components are shadcn-based. Import them from @/components/ui/. Use TailwindCSS utility classes. The color theme is dark slate with amber/gold accents — defined in client/src/index.css.
Key Files
State and Storage
Backend
- Orders → Google Sheets (columns A-R)
- Order counters →
data/orderCounters.json - Push subscriptions → In-memory (lost on restart; admin must re-subscribe)
- Events cache → In-memory with 5 min TTL
Frontend
- Form data →
localStorage(auto-saved, cleared on submit) - Image cache → React state (
Map<string, string>)
Pricing Logic
Do not change pricing without updating all of these:
client/src/components/NumberPadV2.tsx—SIZESarrayclient/src/pages/order-form.tsx—calculateItemTotal,calculate4x6Discount,PHOTO_SIZES- The “3 for 4
Adding a New API Endpoint
- Add the route handler in
server/routes.tsinsideregisterRoutes() - Use
googleSheetsOrderServicefor any order reads/writes - Validate input with Zod
- Call from the frontend with
apiRequestfrom@/lib/queryClient
Common Pitfalls
useToastis at@/hooks/use-toast(not from shadcn directly)- Image preview requires photo number ≥ 4 digits and a valid event slug (not
"CPS") - USPS address validation requires
stateto be exactly 2 uppercase letters - The admin manifest is at
/admin-manifest.webmanifest— different from the customer PWA manifest @shared/schemaresolves via tsconfig path alias — do not use relative../../shared/schema
Recent additions & gotchas
- dotenv must be imported at the top of
server/index.ts(or any entry) so.envvalues are available in dev. Missing it causes mysterious undefined credentials. - The Replit runtime-error-modal plugin in
vite.config.tscaused recurring “Pre-transform error: Failed to parse JSON file” logs and broken HMR when running locally. It’s now only loaded whenREPL_IDis defined. - HMR configuration in
server/vite.tsmust specifyclientPort(set to 5000) or the injected WS URL ends upws://localhost:undefined, preventing hot reload. /api/albumsneeds to be a wrapper around/event(PHP backend) since the backend has no real albums endpoint; the server maps event objects to the album shape consumed by both frontend andgoogleSheetsService.