Skip to content

Commit d66c4bb

Browse files
committed
docs: update instructions with frontend and python guidelines
- Added detailed organization guidelines for frontend (web/app/) and Python app/job structure. - Clarified API call patterns and server/client loader practices for React Router. - Updated pytest and FastAPI best practices. - Improved clarity and added sections for easier onboarding and consistency. Generated-by: aiautocommit
1 parent d39e4b2 commit d66c4bb

3 files changed

Lines changed: 111 additions & 118 deletions

File tree

.cursor/rules/pytest-integration-tests.mdc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ alwaysApply: false
99
- Here's an example of how to create + persist a factory `DistributionFactory.build(domain=PYTHON_TEST_SERVER_HOST).save()`
1010
- Add the `server` factory to each test
1111
- Use the `faker` factory to generate emails, etc.
12+
- Don't add obvious `assert` descriptions

.cursor/rules/typescript.mdc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@ alwaysApply: false
1010
- Use `lib/` for generic code, `utils/` for project utilities, `hooks/` for React hooks, and `helpers/` for page-specific helpers.
1111
- Prefer `function theName() {` over `const theName = () =>`
1212
- Use `import { invariant } from @epic-web/invariant` instead of another invariant library
13+
14+
Here's how frontend code is organized in `web/app/`:
15+
16+
- `lib/` not specific to the project. This code could be a separate package at some point.
17+
- `utils/` project-specific code, but not specific to a particular page.
18+
- `helpers/` page- or section-specific code that is not a component, hook, etc.
19+
- `hooks/` react hooks.
20+
- `configuration/` providers, library configuration, and other setup code.
21+
- `components/` react components.
22+
- `ui/` reusable ShadCN UI components (buttons, forms, etc.).
23+
- `shared/` components shared across multiple pages.
24+
- create additional folders for route- or section-specific components.

instructions.md

Lines changed: 98 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,40 @@ session_id = client_secret_id.split("_secret")[0]
3636
- When running python tests, use an already open terminal and the `pytest` binary.
3737
- If you added models, generate a migration with `just migration {add,delete,update}_model_other_description`
3838

39+
## Fastapi
40+
41+
- When throwing a `HTTPException`, do not add a `detail=` and use a named status code (`status.HTTP_400_BAD_REQUEST`)
42+
- Do not return a `dict`, instead create a `class RouteNameResponse`
43+
44+
## Pytest Integration Tests
45+
46+
- Look to tests/factories.py to generate any required database state
47+
- Here's an example of how to create + persist a factory `DistributionFactory.build(domain=PYTHON_TEST_SERVER_HOST).save()`
48+
- Add the `server` factory to each test
49+
- Use the `faker` factory to generate emails, etc.
50+
- Don't add obvious `assert` descriptions
51+
52+
## Python App
53+
54+
* Files within `app/commands/` should have:
55+
* Are not designed for CLI execution, but instead are interactor-style internal commands.
56+
* Should not be used on the queuing system
57+
* A `perform` function that is the main entry point for the command.
58+
* Look at existing commands for examples of how to structure the command.
59+
* Use `TypeIDType` for any parameters that are IDs of models.
60+
* Files within `app/jobs/` should have:
61+
* Are designed for use on the queuing system.
62+
* A `perform` function that is the main entry point for the job.
63+
* Look at existing jobs for examples of how to structure the job.
64+
* Use `TypeIDType | str` for any parameters that are IDs of models.
65+
* When referencing a command, use the full-qualified name, e.g. `app.commands.transcript_deletion.perform`.
66+
* When queuing a job or `perform`ing it in a test, use the full-qualified name, e.g. `app.jobs.transcript_deletion.perform`.
67+
68+
## Python Route Tests
69+
70+
- Polyfactory is the [factory](tests/factories.py) library in use. `ModelNameFactory.build()` is how you generate factories.
71+
- Use `assert_status(response)` to check the response of a client
72+
3973
## Python
4074

4175
When writing Python:
@@ -100,33 +134,64 @@ class Distribution(
100134
return self.where(Order.distribution_id == self.id).count()
101135
```
102136

103-
## Python App
137+
## React Router
104138

105-
* Files within `app/commands/` should have:
106-
* Are not designed for CLI execution, but instead are interactor-style internal commands.
107-
* Should not be used on the queuing system
108-
* A `perform` function that is the main entry point for the command.
109-
* Look at existing commands for examples of how to structure the command.
110-
* Use `TypeIDType` for any parameters that are IDs of models.
111-
* Files within `app/jobs/` should have:
112-
* Are designed for use on the queuing system.
113-
* A `perform` function that is the main entry point for the job.
114-
* Look at existing jobs for examples of how to structure the job.
115-
* Use `TypeIDType | str` for any parameters that are IDs of models.
116-
* When referencing a command, use the full-qualified name, e.g. `app.commands.transcript_deletion.perform`.
117-
* When queuing a job or `perform`ing it in a test, use the full-qualified name, e.g. `app.jobs.transcript_deletion.perform`.
139+
- You are using the latest version of React Router (v7).
140+
- The primary export in a routes file should specify `loaderData` like `export default function RouteNamePage({ loaderData }: Route.ComponentProps)`. `loaderData` is the return value from `clientLoader`.
141+
- Use `href("/products/:id", { id: "abc123" })` to generate a url path for a route managed by the application.
142+
- Look at [routes.ts](mdc:web/app/routes.ts) to determine what routes and path parameters exist.
143+
- Use `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs)` to define a `clientLoader` on a route.
144+
- Do not define `Route.*` types, these are autogenerated and can be imported from `import type { Route } from "./+types/routeFileName"`
145+
- If URL parameters or query string values need to be checked before rendering the page, do this in a `clientLoader` and not in a `useEffect`
146+
- Never worry about generating types using `pnpm`
147+
- Use [`<AllMeta />`](web/app/components/shared/AllMeta.tsx) instead of MetaFunction or individual `<meta />` tags
148+
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`)
118149

119-
## Pytest Integration Tests
150+
```typescript
151+
const [searchParams, _setSearchParams] = useSearchParams();
152+
// searchParams contains the value of all query string parameters
153+
const queryStringValue = searchParams.get("theQueryStringParam")
154+
```
120155

121-
- Look to tests/factories.py to generate any required database state
122-
- Here's an example of how to create + persist a factory `DistributionFactory.build(domain=PYTHON_TEST_SERVER_HOST).save()`
123-
- Add the `server` factory to each test
124-
- Use the `faker` factory to generate emails, etc.
156+
### Loading Mock Data
125157

126-
## Fastapi
158+
Don't load mock data in the component function with `useEffect`. Instead, load data in a `clientLoader`:
127159

128-
- When throwing a `HTTPException`, do not add a `detail=` and use a named status code (`status.HTTP_400_BAD_REQUEST`)
129-
- Do not return a `dict`, instead create a `class RouteNameResponse`
160+
```typescript
161+
162+
// in mock.ts
163+
export async function getServerData(options: any) {
164+
// ...
165+
}
166+
167+
// in web/app/routes/**/*.ts
168+
export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {
169+
// no error reporting is needed, this will be handled by the `getServerData`
170+
// mock loading functions should return result in a `data` key
171+
const { data } = await getServerData({ /* ... */ })
172+
173+
// the return result here is available in `loaderData`
174+
return data
175+
}
176+
```
177+
178+
### How to use clientLoader
179+
180+
- `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {`
181+
- Load any server data required for page load here, not in the component function.
182+
- Use `return redirect(href("/the/url"))` to redirect users
183+
- Use [getQueryParam](web/app/lib/utils.ts) to get query string variables
184+
- `throw new Response` if you need to mimic a 400, 500, etc error
185+
- `loaderArgs` and all sub-objects are all fully typed
186+
- `loaderArgs.params.id` to get URL parameters
187+
188+
### Using API Data
189+
190+
- `~/configuration/client` re-exports all types and functions from `client/*`. Import from `~/configuration/client` instead of anything you find in the `client/` folder/package.
191+
- For each API endpoint, there's a fully typed async function that can be used to call it. Never attempt to call an API endpoint directly.
192+
- When using an import from `~/configuration/client`:
193+
- use `body:` for request params
194+
- always `const { data, error } = await theCall()`
130195

131196
## React
132197

@@ -199,69 +264,6 @@ return (
199264
)
200265
```
201266

202-
## React Router
203-
204-
- You are using the latest version of React Router (v7).
205-
- The primary export in a routes file should specify `loaderData` like `export default function RouteNamePage({ loaderData }: Route.ComponentProps)`. `loaderData` is the return value from `clientLoader`.
206-
- Use `href("/products/:id", { id: "abc123" })` to generate a url path for a route managed by the application.
207-
- Look at [routes.ts](mdc:web/app/routes.ts) to determine what routes and path parameters exist.
208-
- Use `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs)` to define a `clientLoader` on a route.
209-
- Do not define `Route.*` types, these are autogenerated and can be imported from `import type { Route } from "./+types/routeFileName"`
210-
- If URL parameters or query string values need to be checked before rendering the page, do this in a `clientLoader` and not in a `useEffect`
211-
- Never worry about generating types using `pnpm`
212-
- Use [`<AllMeta />`](web/app/components/shared/AllMeta.tsx) instead of MetaFunction or individual `<meta />` tags
213-
- Use the following pattern to reference query string values (i.e. `?theQueryStringParam=value`)
214-
215-
```typescript
216-
const [searchParams, _setSearchParams] = useSearchParams();
217-
// searchParams contains the value of all query string parameters
218-
const queryStringValue = searchParams.get("theQueryStringParam")
219-
```
220-
221-
### Loading Mock Data
222-
223-
Don't load mock data in the component function with `useEffect`. Instead, load data in a `clientLoader`:
224-
225-
```typescript
226-
227-
// in mock.ts
228-
export async function getServerData(options: any) {
229-
// ...
230-
}
231-
232-
// in web/app/routes/**/*.ts
233-
export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {
234-
// no error reporting is needed, this will be handled by the `getServerData`
235-
// mock loading functions should return result in a `data` key
236-
const { data } = await getServerData({ /* ... */ })
237-
238-
// the return result here is available in `loaderData`
239-
return data
240-
}
241-
```
242-
243-
### How to use clientLoader
244-
245-
- `export async function clientLoader(loaderArgs: Route.ClientLoaderArgs) {`
246-
- Load any server data required for page load here, not in the component function.
247-
- Use `return redirect(href("/the/url"))` to redirect users
248-
- Use [getQueryParam](web/app/lib/utils.ts) to get query string variables
249-
- `throw new Response` if you need to mimic a 400, 500, etc error
250-
- `loaderArgs` and all sub-objects are all fully typed
251-
- `loaderArgs.params.id` to get URL parameters
252-
253-
### Using API Data
254-
255-
- `~/configuration/client` re-exports all types and functions from `client/*`. Import from `~/configuration/client` instead of anything you find in the `client/` folder/package.
256-
- For each API endpoint, there's a fully typed async function that can be used to call it. Never attempt to call an API endpoint directly.
257-
- When using an import from `~/configuration/client`:
258-
- use `body:` for request params
259-
- always `const { data, error } = await theCall()`
260-
261-
## React Router Client Loader
262-
263-
Do this in a `clientLoader` and use `loaderData` to render the component. DO NOT create mock data, new interfaces, or mock data loader functions. Instead, assume `loaderData` has all of the data you need to render the component.
264-
265267
## Shell
266268

267269
- Assume zsh for any shell scripts. The latest version of modern utilities like ripgrep (rg), fdfind (fd), bat, httpie (http), zq (zed), jq, procs, rsync are installed and you can request I install additional utilities.
@@ -274,37 +276,15 @@ Do this in a `clientLoader` and use `loaderData` to render the component. DO NOT
274276
- Prefer `function theName() {` over `const theName = () =>`
275277
- Use `import { invariant } from @epic-web/invariant` instead of another invariant library
276278

277-
## Typescript Docstring
278-
279-
Add a file-level docstring with a simple description of what this file does and where this is used.
280-
281-
## Secrets
282-
283-
Here's how environment variables are managed in this application:
284-
285-
- `.envrc` entry point to load the correct env stack. Should not contain secrets and should be simple some shell logic and direnv stdlib calls.
286-
- `.env` common configuration for all systems. No secrets. No dotenv/custom scripts. Just `export`s to modify core configuration settings like `export TZ=UTC`.
287-
- `.env.local` overrides across all environments (dev and test). Useful for things like 1Password service account token and database hosts which mutate the logic followed in `.env.shared`. Not committed to source control.
288-
- `.env.shared` This contains the bulk of your system configuration. Shared across test, CI, dev, etc but not production.
289-
- `.env.shared.local` Override `.env.shared` configuration locally. Not committed to source.
290-
- `.env.dev.local` configuration overrides for non-test environments. `PYTHONBREAKPOINT`, `LOG_LEVEL`, etc. Most of your environment changes end up happening here.
291-
- `.env.test` test-only environment variables (`PYTHON_ENV=test`). This file should generally be short.
292-
- `.env.production.{backend,frontend}` for most medium-sized projects you'll have separate frontend and backend systems (even if your frontend is SPA, which I'm a fan of). These two files enable you to document the variables required to build (in the case of a SPA frontend) or run (in the case of a python backend) your system in production.
293-
- `*.local` files have a `-example` variant which is committed to version control. These document helpful environment variables for local development.
294-
- When writing TypeScript/JavaScript/React, use `requireEnv("THE_ENV_VAR_NAME")` to read an environment variable. `import {requireEnv} from '~/utils/environment'`
295-
296-
## Implement Fastapi Routes
297-
298-
The file docstring contains a description of the FastAPI routes we need to implement. Implement these routes.
299-
300-
Avoid implementing any Stripe logic right now. I will do that later. Leave TODOs for this and other areas where you are very unsure of what to do.
301-
302-
## Python Route Tests
303-
304-
- Polyfactory is the [factory](tests/factories.py) library in use. `ModelNameFactory.build()` is how you generate factories.
305-
- Use `assert_status(response)` to check the response of a client
306-
307-
## Refactor On Instructions
308-
309-
Refactor this code following all the established coding rules. Carefully review each rule.
279+
Here's how frontend code is organized in `web/app/`:
280+
281+
- `lib/` not specific to the project. This code could be a separate package at some point.
282+
- `utils/` project-specific code, but not specific to a particular page.
283+
- `helpers/` page- or section-specific code that is not a component, hook, etc.
284+
- `hooks/` react hooks.
285+
- `configuration/` providers, library configuration, and other setup code.
286+
- `components/` react components.
287+
- `ui/` reusable ShadCN UI components (buttons, forms, etc.).
288+
- `shared/` components shared across multiple pages.
289+
- create additional folders for route- or section-specific components.
310290

0 commit comments

Comments
 (0)