Skip to content

Commit 18d232b

Browse files
committed
create ai guidelines files
1 parent 0d71a8b commit 18d232b

File tree

2 files changed

+774
-0
lines changed

2 files changed

+774
-0
lines changed

.github/copilot-instructions.md

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
# CORD API Development Guidelines
2+
3+
This document provides guidelines for developers working on the CORD API v3 project, a backend built with NestJS, GraphQL, and Neo4j/Gel, using NodeJS and Docker. The API serves the CORD Field front-end.
4+
5+
## Overview
6+
7+
The CORD API v3 provides [describe functionality briefly, e.g., data management for field operations; replace with specific details if available]. It runs by default on `http://localhost:3000`. For a complete setup guide, see the [cord-docs wiki](https://github.com/SeedCompany/cord-docs/wiki#new-hire-on-boarding).
8+
9+
## Build/Configuration Instructions
10+
11+
### Prerequisites
12+
13+
1. Docker (install from official website, not Homebrew).
14+
2. NodeJS (version as specified in `package.json`, currently >= 20.6).
15+
- Check version with `node -v`. Upgrade if compatibility issues arise.
16+
3. Corepack enabled (`corepack enable`).
17+
4. Yarn (managed via Corepack).
18+
5. Gel CLI (`brew install geldata/tap/gel-cli`).
19+
20+
### Setup
21+
22+
1. Install dependencies:
23+
24+
```bash
25+
yarn
26+
```
27+
28+
2. Start the Neo4j database using Docker:
29+
30+
```bash
31+
docker-compose up -d db
32+
```
33+
34+
3. Set up Gel (next-gen database replacing Neo4j):
35+
```bash
36+
gel project init
37+
yarn gel:gen
38+
```
39+
40+
### Development
41+
42+
1. Start the development server:
43+
44+
```bash
45+
yarn start:dev
46+
```
47+
48+
2. For debugging:
49+
50+
```bash
51+
yarn start:debug
52+
```
53+
54+
3. For production:
55+
56+
```bash
57+
yarn start:prod
58+
```
59+
60+
4. Generate GraphQL schema:
61+
62+
```bash
63+
yarn gel:gen
64+
```
65+
66+
5. Clean build artifacts:
67+
68+
```bash
69+
yarn clean
70+
```
71+
72+
6. Create database migrations:
73+
74+
```bash
75+
yarn gel:migration
76+
```
77+
78+
7. Generate seed data:
79+
```bash
80+
yarn gel:seed
81+
```
82+
83+
## Testing Information
84+
85+
### Testing Framework
86+
87+
The project uses Jest for testing:
88+
89+
- Unit tests: Located in `tests` folders alongside source code in `src/components`, with `.spec.ts` or `.test.ts` extensions.
90+
- End-to-end (E2E) tests: Located in `test` directory, with `.e2e-spec.ts` extension.
91+
92+
### Running Tests
93+
94+
1. Run unit tests:
95+
96+
```bash
97+
yarn test
98+
```
99+
100+
2. Run E2E tests:
101+
102+
```bash
103+
yarn test:e2e
104+
```
105+
106+
3. Run tests with coverage:
107+
```bash
108+
yarn test:cov
109+
```
110+
111+
### Writing Tests
112+
113+
#### Unit Tests
114+
115+
Unit tests should be placed in `tests` folders alongside the service or resolver, with a `.spec.ts` or `.test.ts` extension. For example:
116+
117+
- `src/components/user/user.service.ts``src/components/user/tests/user.service.spec.ts`
118+
- `src/common/util.ts``src/common/tests/util.test.ts`
119+
120+
Example unit test:
121+
122+
```typescript
123+
// src/common/tests/util.test.ts
124+
import { firstOr } from '../util';
125+
126+
describe('firstOr', () => {
127+
it('should return the first item in the array', () => {
128+
const items = [1, 2, 3];
129+
const result = firstOr(items, () => new Error('Array is empty'));
130+
expect(result).toBe(1);
131+
});
132+
133+
it('should throw an error if the array is empty', () => {
134+
const items: number[] = [];
135+
expect(() => firstOr(items, () => new Error('Array is empty'))).toThrow(
136+
'Array is empty',
137+
);
138+
});
139+
});
140+
```
141+
142+
#### E2E Tests
143+
144+
E2E tests should be placed in the `test` directory with an `.e2e-spec.ts` extension, testing GraphQL API endpoints.
145+
146+
Example E2E test:
147+
148+
```typescript
149+
// test/user.e2e-spec.ts
150+
import { createTestApp, gql, TestApp } from './utility';
151+
152+
describe('User API', () => {
153+
let app: TestApp;
154+
155+
beforeAll(async () => {
156+
app = await createTestApp();
157+
});
158+
159+
afterAll(async () => {
160+
await app.close();
161+
});
162+
163+
it('should fetch a user by ID', async () => {
164+
const result = await app.graphql.query({
165+
query: gql`
166+
query GetUser($id: ID!) {
167+
user(id: $id) {
168+
id
169+
name
170+
}
171+
}
172+
`,
173+
variables: { id: '123' },
174+
});
175+
expect(result.user).toEqual({ id: '123', name: 'Alice' });
176+
});
177+
});
178+
```
179+
180+
## Additional Development Information
181+
182+
### Code Style
183+
184+
The project uses ESLint and Prettier for code formatting and linting:
185+
186+
- Run `yarn lint` to check and fix linting issues.
187+
- Run `yarn format` to format code using Prettier.
188+
- Enforce TypeScript strict mode (`tsconfig.json` with `strict: true`).
189+
190+
### GraphQL
191+
192+
The project uses NestJS with GraphQL (graphql-yoga):
193+
194+
- GraphQL schema is generated in `schema.graphql` via `yarn gel:gen`.
195+
- Resolvers are defined in `*.resolver.ts` files in `src/components`.
196+
- DTOs (Data Transfer Objects) are defined in `*.dto.ts` files.
197+
- Only access properties defined in DTOs or interfaces.
198+
199+
### Database
200+
201+
The API is transitioning from Neo4j to Gel:
202+
203+
- Neo4j is used with the `cypher-query-builder` library for queries.
204+
- Gel is the next-gen database replacing Neo4j.
205+
- Create migrations with `yarn gel:migration` to update the database schema.
206+
- Generate seed data with `yarn gel:seed` for testing or development.
207+
208+
### Project Structure
209+
210+
- `src/`: Source code
211+
- `src/common/`: Common utilities, decorators, and TypeScript interfaces.
212+
- `src/components/`: Feature-specific modules (e.g., `user`, `order`), containing resolvers, services, DTOs, and tests subfolders.
213+
- `src/core/`: Core functionality, such as database connections and GraphQL setup.
214+
- `test/`: E2E tests
215+
- `test/utility/`: Test utilities (e.g., test app setup).
216+
- `dbschema/`: Database schema definitions and migrations.
217+
218+
### Coding Standards
219+
220+
- Use camelCase for variables, functions, and methods; ensure names are descriptive.
221+
- Use PascalCase for classes, interfaces, and enums.
222+
- Use kebab-case for new folders and files.
223+
- Use single quotes for strings, 2 spaces for indentation.
224+
- Prefer async/await for asynchronous operations.
225+
- Use `const` for constants, minimize `let` usage (e.g., except in try/catch).
226+
- Use destructuring for objects/arrays, template literals for strings.
227+
- Follow SOLID principles for modular, reusable, maintainable code.
228+
- Avoid code duplication, deeply nested statements, hard-coded values.
229+
- Use constants/enums instead of magic numbers/strings.
230+
- Avoid mutations:
231+
- Prefer `const` over `let`.
232+
- Use spread syntax (e.g., `{ ...object, foo: 'bar' }`) instead of modifying objects.
233+
- Use strict TypeScript:
234+
- Define all object shapes in DTOs (`*.dto.ts`) or interfaces in `src/common`.
235+
- Use type guards for safe property access (e.g., `if ('foo' in obj)`).
236+
237+
### GraphQL Guidelines
238+
239+
- Define resolvers in `*.resolver.ts` with clear input/output types.
240+
- Use DTOs for input validation and response shaping.
241+
- Avoid overfetching; include only necessary fields in queries.
242+
- Handle errors explicitly using NestJS’s error handling (e.g., `throw new BadRequestException()`).
243+
- Use `// ai example` to mark well-structured resolvers or DTOs.
244+
245+
### Database Guidelines
246+
247+
- Write Cypher queries for Neo4j using `cypher-query-builder` for type safety.
248+
- Create Gel migrations for schema changes (`yarn gel:migration`).
249+
- Validate query results against defined DTOs or interfaces.
250+
- Avoid direct database mutations outside services; encapsulate in `*.service.ts`.
251+
252+
### Version Control
253+
254+
- Create branches: `type/MondayTicketID-description` (e.g., `feature/1234-new-endpoint`).
255+
- Write commit messages:
256+
- First line: `[TicketID] - [Short Title] - Imperative verb` (e.g., `0654 - Add user endpoint`).
257+
- Optional body: Explain why the change is needed.
258+
- Create PRs for single changes:
259+
- Link Monday ticket in description.
260+
- Use bite-sized, logical commits.
261+
- Demo functionality to reviewers.
262+
- Check entire PR for applicability.
263+
- Verify no incorrect property access; properties must match DTOs or interfaces.
264+
265+
### Security Practices
266+
267+
- Sanitize GraphQL inputs to prevent injection attacks.
268+
- Implement role-based access control (RBAC) in resolvers via GraphQL context.
269+
- Use environment variables for sensitive data (e.g., database credentials).
270+
- Configure secure CORS settings in `src/core`.
271+
- Validate all external data (e.g., API inputs, database results) against DTOs.
272+
- Check dependencies with Snyk before adding via Yarn.
273+
274+
### Common Errors to Avoid
275+
276+
- **Accessing Non-Existent Properties**:
277+
- Never assume properties exist without type verification.
278+
- Reference DTOs in `*.dto.ts` or interfaces in `src/common`.
279+
- Use type guards (e.g., `if ('foo' in obj)`) or optional chaining (`?.`) for dynamic data.
280+
- Example (Correct):
281+
```typescript
282+
// ai type-safety Safe property access with type guard
283+
interface User {
284+
name?: string;
285+
}
286+
function getName(user: User): string {
287+
return 'name' in user && user.name ? user.name : 'Unknown';
288+
}
289+
```
290+
_Why_: Prevents runtime errors by checking property existence.
291+
- Example (Incorrect):
292+
```typescript
293+
// ai anti-pattern Assumes non-existent property
294+
interface User {
295+
name?: string;
296+
}
297+
function getName(user: User): string {
298+
return user.name; // Error: Property 'name' may be undefined
299+
}
300+
```
301+
_Why_: Causes runtime errors due to unverified property access.
302+
- **Unvalidated GraphQL Inputs**:
303+
- Always validate inputs using DTOs or class-validator in resolvers.
304+
- Use `// ai anti-pattern` to mark unvalidated inputs.
305+
306+
### Type Checking
307+
308+
Run `yarn type-check` to check for TypeScript errors without compiling the code.
309+
310+
### Debugging
311+
312+
- Use `yarn start:debug` to start the server in debug mode with breakpoints.
313+
- Use Jest debugger for tests: `yarn test --selectProjects Unit` with debugger attached.
314+
- Enable NestJS logging in `src/core` for request/response debugging.
315+
- Use Neo4j Desktop or Gel CLI (`gel query`) to inspect database state.
316+
317+
### Tagged Comments
318+
319+
- Use `// ai tag` to mark code for reference:
320+
- `example`: Best practice or model code.
321+
- `edge-case`: Necessary deviation from standards.
322+
- `best-practice`: Adherence to coding standards.
323+
- `anti-pattern`: Code to avoid (pending refactor).
324+
- `todo`: Needs improvement or refactoring.
325+
- `workaround`: Temporary fix for a limitation.
326+
- `performance`: Optimized code.
327+
- `security`: Security-critical code.
328+
- `test`: Exemplary test case.
329+
- `type-safety`: Safe property access.
330+
- Optionally add notes after the tag (e.g., `// ai example Type-safe resolver`).
331+
- Search tags with `git grep "ai "` to collect examples.
332+
333+
## Project Structure
334+
335+
- Place GraphQL resolvers in `src/components/*/resolver.ts`.
336+
- Place services in `src/components/*/service.ts`.
337+
- Place DTOs in `src/components/*/dto/*.dto.ts`.
338+
- Place interfaces in `src/common`.
339+
- Place unit tests in `src/components/*/tests/*.spec.ts`.
340+
- Place E2E tests in `test/*.e2e-spec.ts`.
341+
342+
## Coding Standards
343+
344+
- Use camelCase for variables/functions, PascalCase for classes/interfaces/DTOs, kebab-case for files/folders.
345+
- Use single quotes, 2-space indentation, async/await, and `const` over `let`.
346+
- Use strict TypeScript with DTOs (`*.dto.ts`) or interfaces (`src/common`).
347+
- Example:
348+
```typescript
349+
// ai type-safety Safe property access
350+
interface User {
351+
name?: string;
352+
}
353+
function getName(user: User): string {
354+
return 'name' in user && user.name ? user.name : 'Unknown';
355+
}
356+
```
357+
358+
## GraphQL and Database
359+
360+
- Define resolvers in `*.resolver.ts` with DTOs and `class-validator`.
361+
- Use `cypher-query-builder` for Neo4j queries in `*.service.ts`.
362+
- Generate schema with `yarn gel:gen`.
363+
- Example:
364+
```typescript
365+
// ai example Type-safe resolver
366+
@Resolver(() => UserDto)
367+
export class UserResolver {
368+
@Query(() => UserDto)
369+
async user(@Args('id') id: string): Promise<UserDto> {
370+
return this.userService.findById(id);
371+
}
372+
}
373+
```
374+
375+
## Testing
376+
377+
- Use Jest for tests in `src/components/*/tests/*.spec.ts` (unit) and `test/*.e2e-spec.ts` (E2E).
378+
- Example:
379+
```typescript
380+
// ai test Unit test
381+
describe('UserService', () => {
382+
it('should return user by ID', async () => {
383+
const result = await userService.findById('123');
384+
expect(result).toEqual({ id: '123', name: 'Alice' });
385+
});
386+
});
387+
```

0 commit comments

Comments
 (0)