@@ -11,6 +11,7 @@ Thank you for your interest in contributing to Vaultix! This document provides g
1111- [ Coding Standards] ( #coding-standards )
1212- [ Testing] ( #testing )
1313- [ Documentation] ( #documentation )
14+ - [ API Versioning Strategy] ( #api-versioning-strategy )
1415- [ Community] ( #community )
1516
1617## Code of Conduct
@@ -68,13 +69,15 @@ chore/update-dependencies # Maintenance tasks
6869```
6970
7071** Examples:**
72+
7173- ` feat/add-dispute-resolution-modal `
7274- ` fix/wallet-connection-timeout `
7375- ` docs/contributing-guidelines `
7476
7577### Making Changes
7678
77791 . ** Create a branch** :
80+
7881 ``` bash
7982 git checkout -b feat/your-feature-name
8083 ```
@@ -84,6 +87,7 @@ chore/update-dependencies # Maintenance tasks
84873 . ** Write tests** : Add tests for new functionality
8588
86894 . ** Run checks locally** :
90+
8791 ``` bash
8892 pnpm turbo run lint test build
8993 ```
@@ -107,6 +111,7 @@ footer (optional)
107111```
108112
109113** Types:**
114+
110115- ` feat ` : New feature
111116- ` fix ` : Bug fix
112117- ` docs ` : Documentation changes
@@ -163,27 +168,33 @@ When creating a PR, use this template:
163168
164169``` markdown
165170## Description
171+
166172Brief description of changes and what problem this solves
167173
168174## Related Issue
175+
169176Closes #123
170177
171178## Type of Change
179+
172180- [ ] Bug fix (non-breaking change which fixes an issue)
173181- [ ] New feature (non-breaking change which adds functionality)
174182- [ ] Breaking change (fix or feature that would cause existing functionality to change)
175183- [ ] Documentation update
176184
177185## Testing Done
186+
178187- [ ] Unit tests added/updated
179188- [ ] E2E tests added/updated
180189- [ ] Manually tested locally
181190
182191## Screenshots (if UI changes)
192+
183193Before: [ screenshot]
184194After: [ screenshot]
185195
186196## Checklist
197+
187198- [ ] My code follows the project's coding standards
188199- [ ] I have run lint and tests locally
189200- [ ] I have updated documentation as needed
@@ -219,13 +230,13 @@ After: [screenshot]
219230async createEscrow (data : CreateEscrowDto ): Promise < Escrow > {
220231 // Validate input
221232 await this.validateEscrowData(data);
222-
233+
223234 // Create escrow record
224235 const escrow = await this .escrowRepository .create (data );
225-
236+
226237 // Emit event
227238 await this.eventEmitter.emit(' escrow.created' , escrow);
228-
239+
229240 return escrow;
230241}
231242```
@@ -268,6 +279,7 @@ pub fn create_escrow(
268279Organize code logically following the existing structure:
269280
270281** Backend:**
282+
271283```
272284apps/backend/src/
273285├── modules/ # Feature modules (auth, escrow, stellar, etc.)
@@ -279,6 +291,7 @@ apps/backend/src/
279291```
280292
281293** Frontend:**
294+
282295```
283296apps/frontend/
284297├── app/ # Next.js app router pages
@@ -323,7 +336,7 @@ cd apps/onchain && cargo test
323336** Backend Unit Test** (Jest):
324337
325338``` typescript
326- describe (' EscrowService' , () => {
339+ describe (" EscrowService" , () => {
327340 let service: EscrowService ;
328341
329342 beforeEach (async () => {
@@ -334,26 +347,28 @@ describe('EscrowService', () => {
334347 service = module .get <EscrowService >(EscrowService );
335348 });
336349
337- it (' should create escrow with valid data' , async () => {
350+ it (" should create escrow with valid data" , async () => {
338351 const escrowData = {
339352 amount: 100 ,
340- recipient: ' test-address' ,
341- milestones: [' Milestone 1' ],
353+ recipient: " test-address" ,
354+ milestones: [" Milestone 1" ],
342355 };
343-
356+
344357 const result = await service .create (escrowData );
345-
358+
346359 expect (result ).toBeDefined ();
347- expect (result .status ).toBe (' pending' );
360+ expect (result .status ).toBe (" pending" );
348361 expect (result .amount ).toBe (100 );
349362 });
350363
351- it (' should reject escrow with invalid amount' , async () => {
352- const invalidData = { /* ... */ };
353-
354- await expect (service .create (invalidData ))
355- .rejects
356- .toThrow (EscrowValidationError );
364+ it (" should reject escrow with invalid amount" , async () => {
365+ const invalidData = {
366+ /* ... */
367+ };
368+
369+ await expect (service .create (invalidData )).rejects .toThrow (
370+ EscrowValidationError ,
371+ );
357372 });
358373});
359374```
@@ -367,24 +382,24 @@ import { CreateEscrowForm } from '@/components/escrow/create-escrow-form';
367382describe (' CreateEscrowForm' , () => {
368383 it (' submits form with valid data' , async () => {
369384 const mockSubmit = jest .fn ();
370-
385+
371386 render (<CreateEscrowForm onSubmit ={mockSubmit} />);
372-
387+
373388 // Fill form
374389 fireEvent .change (screen .getByLabelText (/ amount/ i ), {
375390 target: { value: ' 100' }
376391 });
377-
392+
378393 fireEvent .change (screen .getByLabelText (/ recipient/ i ), {
379394 target: { value: ' GABC...DEF' }
380395 });
381-
396+
382397 // Submit
383398 fireEvent .click (screen .getByText (' Create Escrow' ));
384-
399+
385400 // Wait for submission
386401 await screen .findByText (/ escrow created successfully/ i );
387-
402+
388403 expect (mockSubmit ).toHaveBeenCalledWith (
389404 expect .objectContaining ({
390405 amount: 100 ,
@@ -395,10 +410,10 @@ describe('CreateEscrowForm', () => {
395410
396411 it (' shows validation errors for invalid input' , async () => {
397412 render (<CreateEscrowForm onSubmit ={jest .fn ()} />);
398-
413+
399414 // Submit empty form
400415 fireEvent .click (screen .getByText (' Create Escrow' ));
401-
416+
402417 expect (await screen .findByText (/ amount is required/ i ))
403418 .toBeInTheDocument ();
404419 });
@@ -412,20 +427,20 @@ describe('CreateEscrowForm', () => {
412427fn test_create_escrow () {
413428 let env = Env :: default ();
414429 env . mock_all_auths ();
415-
430+
416431 let contract_id = env . register_contract (None , VaultixEscrow );
417432 let depositor = Address :: generate (& env );
418433 let recipient = Address :: generate (& env );
419434 let amount = 1000_000_000 ; // 1 XLM in stroops
420-
435+
421436 // Create escrow
422437 let escrow_id = VaultixEscrowClient :: new (& env , & contract_id )
423438 . create_escrow (& depositor , & recipient , & amount );
424-
439+
425440 // Verify escrow was created
426441 let escrow = VaultixEscrowClient :: new (& env , & contract_id )
427442 . get_escrow (& escrow_id );
428-
443+
429444 assert_eq! (escrow . depositor, depositor );
430445 assert_eq! (escrow . recipient, recipient );
431446 assert_eq! (escrow . amount, amount );
@@ -435,6 +450,7 @@ fn test_create_escrow() {
435450### Test Coverage Goals
436451
437452Aim for high coverage on critical paths:
453+
438454- ✅ Authentication flows (wallet connect, JWT)
439455- ✅ Escrow creation and fund release
440456- ✅ Milestone tracking and approval
@@ -450,13 +466,13 @@ Aim for high coverage on critical paths:
450466``` typescript
451467/**
452468 * Validates and processes escrow milestone completion
453- *
469+ *
454470 * @param escrowId - The ID of the escrow to update
455471 * @param milestoneIndex - Index of the milestone to complete
456472 * @param proofData - Optional proof of milestone completion
457- *
473+ *
458474 * @returns Updated escrow entity
459- *
475+ *
460476 * @throws {NotFoundError} If escrow doesn't exist
461477 * @throws {InvalidStateError} If escrow is not in active state
462478 */
@@ -489,7 +505,7 @@ async completeMilestone(
489505/// Returns `Error::Unauthorized` if caller is not authorized
490506/// Returns `Error::InvalidState` if escrow conditions not met
491507#[contractmethod]
492- pub fn release_funds (e : & Env , escrow_id : u64 , caller : Address )
508+ pub fn release_funds (e : & Env , escrow_id : u64 , caller : Address )
493509 -> Result <Bytes , Error > {
494510 // Implementation
495511}
@@ -498,12 +514,113 @@ pub fn release_funds(e: &Env, escrow_id: u64, caller: Address)
498514### Updating README
499515
500516Update README.md when:
517+
501518- Adding new features or capabilities
502519- Changing setup requirements or prerequisites
503520- Modifying architecture or repository structure
504521- Adding new configuration options
505522- Updating deployment instructions
506523
524+ ## API Versioning Strategy
525+
526+ Vaultix uses ** URL-based API versioning** to ensure backward compatibility as the platform evolves.
527+
528+ ### Current Version
529+
530+ The current stable API version is ** v1** . All endpoints are prefixed with ` /v1/ ` .
531+
532+ ```
533+ GET /v1/escrows
534+ POST /v1/auth/verify
535+ GET /v1/admin/users
536+ ```
537+
538+ ### Version Lifecycle
539+
540+ | Version | Status | Sunset Date | Notes |
541+ | ------- | ---------- | ----------- | ----------------------------------- |
542+ | v1 | ** Active** | - | Current stable API |
543+ | v2 | Scaffold | - | Placeholder endpoints, not yet live |
544+
545+ ### Backward Compatibility
546+
547+ Unversioned requests (e.g., ` /escrows ` , ` /auth/verify ` ) are automatically rewritten to ` /v1/... ` by the version negotiation middleware. These unversioned endpoints return a ` Sunset ` header indicating the deprecation deadline:
548+
549+ ```
550+ Sunset: 2026-12-31T23:59:59.000Z
551+ Link: </v1/escrows>; rel="successor-version"
552+ X-API-Version: v1
553+ ```
554+
555+ ** All API consumers should migrate to versioned URLs before the sunset date.**
556+
557+ ### Response Headers
558+
559+ | Header | Description |
560+ | --------------- | -------------------------------------------- |
561+ | ` X-API-Version ` | The resolved API version (e.g., ` v1 ` ) |
562+ | ` Sunset ` | RFC 8594 sunset date for deprecated versions |
563+ | ` Deprecation ` | Set to ` true ` when a version is deprecated |
564+ | ` Link ` | Points to the successor version URL |
565+
566+ ### Adding a New Version
567+
568+ 1 . ** Create controllers** with explicit version:
569+
570+ ``` typescript
571+ @Controller ({ path: ' escrows' , version: ' 2' })
572+ export class EscrowV2Controller { ... }
573+ ```
574+
575+ 2 . ** Register in a module** and import in ` AppModule ` .
576+
577+ 3 . ** Update the version sunset map** in ` api-version.middleware.ts ` :
578+
579+ ``` typescript
580+ const API_VERSION_SUNSETS: Record <string , string | null > = {
581+ v1: null , // active
582+ v2: null , // active (new)
583+ };
584+ ```
585+
586+ 4 . ** Add to Swagger UI** version selector in ` main.ts ` .
587+
588+ 5 . ** Deprecate old versions** by setting a sunset date:
589+ ``` typescript
590+ v1 : ' 2027-06-30T23:59:59.000Z' , // deprecated
591+ ```
592+
593+ ### Version-Neutral Endpoints
594+
595+ Some endpoints are version-neutral (accessible without a version prefix):
596+
597+ - ` /health ` - Health check probes
598+ - ` /api/docs ` - Swagger UI
599+
600+ Use ` VERSION_NEUTRAL ` in the controller decorator:
601+
602+ ``` typescript
603+ @Controller ({ path: ' health' , version: VERSION_NEUTRAL })
604+ ```
605+
606+ ### V2 Design Goals (Planned)
607+
608+ - Cursor-based pagination (replacing offset-based)
609+ - Envelope responses: ` { data, meta, links } `
610+ - Standardized filtering query parameters
611+ - RFC 7807 Problem Details error format
612+ - HATEOAS links for related resources
613+
614+ ### Frontend Integration
615+
616+ The frontend API client automatically prepends ` /v1 ` to all requests:
617+
618+ ``` typescript
619+ // lib/api-client.ts
620+ const API_VERSION_PREFIX = " /v1" ;
621+ const url = ` ${API_BASE_URL }${API_VERSION_PREFIX }${path } ` ;
622+ ```
623+
507624## Monorepo Tips
508625
509626### Filtering Commands
@@ -544,13 +661,15 @@ pnpm add axios
544661## Recognition
545662
546663Contributors will be recognized in:
664+
547665- README.md contributors section
548666- Release notes
549667- Annual contributor spotlight
550668
551669## What We're Looking For
552670
553671** High Priority Contributions:**
672+
554673- ✅ Bug fixes (especially issues labeled ` bug ` or ` priority: high ` )
555674- ✅ Test coverage improvements
556675- ✅ Documentation enhancements
@@ -559,6 +678,7 @@ Contributors will be recognized in:
559678- ✅ Security enhancements
560679
561680** Post-MVP Features** (discuss before implementing):
681+
562682- Multi-asset support (custom tokens, USDC)
563683- Advanced analytics dashboard
564684- Mobile applications
0 commit comments