This guide will help you set up and start developing with the Aqua Stark Backend API.
- Node.js
20.10.0(exact version required) - npm or yarn package manager
- Access to:
- Supabase project
- Starknet RPC endpoint
- Cartridge authentication URL
git clone <repository-url>
cd API-Aqua-Starknpm installCreate a .env file in the root directory:
cp .env.example .envEdit .env with your credentials:
# =============================================================================
# SUPABASE CONFIGURATION
# =============================================================================
SUPABASE_URL=your_supabase_url_here
SUPABASE_KEY=your_supabase_anon_key_here
SUPABASE_DB_URL=your_db_url_here
SUPABASE_ANON_KEY=your_anon_key_here
SUPABASE_DB_PASSWORD=your_supabase_password
# =============================================================================
# STARKNET CONFIGURATION
# =============================================================================
STARKNET_RPC=https://starknet-mainnet.public.blastapi.io
STARKNET_CHAIN_ID=SN_MAIN
# =============================================================================
# CARTRIDGE AUTHENTICATION
# =============================================================================
CARTRIDGE_AUTH_URL=https://cartridge.gg/auth
# =============================================================================
# SERVER CONFIGURATION
# =============================================================================
PORT=4000
NODE_ENV=development
# =============================================================================
# DOJO CONFIGURATION (Optional)
# =============================================================================
DOJO_ACCOUNT_ADDRESS=
DOJO_PRIVATE_KEY=
# =============================================================================
# CORS Configuration
# =============================================================================
CORS_ORIGIN=
CORS_CREDENTIALS=true
# =============================================================================
# Shutdown Configuration
# =============================================================================
SHUTDOWN_TIMEOUT=15000
# Check TypeScript compilation
npm run type-check
# Should complete without errorsnpm run devThe server will start on http://localhost:3000 (or your configured PORT).
| Script | Description |
|---|---|
npm run dev |
Start development server with hot-reload |
npm run build |
Compile TypeScript to JavaScript |
npm start |
Start production server |
npm run type-check |
Verify types without compiling |
/src
├── api/ # Route definitions
├── controllers/ # Request handlers
├── services/ # Business logic
├── models/ # Data models
└── core/ # Core system
├── types/ # Type definitions
├── errors/ # Error classes
├── responses/ # Response helpers
├── middleware/ # Middleware
├── utils/ # Utilities
└── config/ # Configuration
// src/models/player.model.ts
export interface Player {
id: string;
address: string;
username?: string;
xp: number;
level: number;
}
export interface CreatePlayerDto {
address: string;
username?: string;
}// src/services/player.service.ts
import { ValidationError, NotFoundError } from '../core/errors';
import type { Player, CreatePlayerDto } from '../models/player.model';
export class PlayerService {
async create(dto: CreatePlayerDto): Promise<Player> {
if (!dto.address) {
throw new ValidationError('Address is required');
}
// Implementation here
return player;
}
async getByAddress(address: string): Promise<Player> {
// Implementation here
if (!player) {
throw new NotFoundError(`Player not found: ${address}`);
}
return player;
}
}// src/controllers/player.controller.ts
import type { FastifyRequest, FastifyReply } from 'fastify';
import type { ControllerResponse } from '../core/types';
import { createSuccessResponse } from '../core/responses';
import { PlayerService } from '../services/player.service';
import type { CreatePlayerDto } from '../models/player.model';
const playerService = new PlayerService();
export async function getPlayer(
request: FastifyRequest<{ Params: { address: string } }>,
reply: FastifyReply
): Promise<ControllerResponse<Player>> {
const { address } = request.params;
const player = await playerService.getByAddress(address);
return createSuccessResponse(player, 'Player retrieved successfully');
}
export async function createPlayer(
request: FastifyRequest<{ Body: CreatePlayerDto }>,
reply: FastifyReply
): Promise<ControllerResponse<Player>> {
const dto = request.body;
const player = await playerService.create(dto);
return createSuccessResponse(player, 'Player created successfully');
}// src/api/player.routes.ts
import type { FastifyInstance } from 'fastify';
import { getPlayer, createPlayer } from '../controllers/player.controller';
export async function playerRoutes(app: FastifyInstance): Promise<void> {
app.get('/api/player/:address', getPlayer);
app.post('/api/player', createPlayer);
}// src/api/index.ts
import type { FastifyInstance } from 'fastify';
import { playerRoutes } from './player.routes';
export async function registerRoutes(app: FastifyInstance): Promise<void> {
await app.register(playerRoutes);
}# GET request
curl http://localhost:3000/api/player/0x123...
# POST request
curl -X POST http://localhost:3000/api/player \
-H "Content-Type: application/json" \
-d '{"address": "0x123...", "username": "Player1"}'- Create a new request
- Set method (GET, POST, etc.)
- Set URL:
http://localhost:3000/api/player/:address - Add headers:
Content-Type: application/json - Add body (for POST): JSON with required fields
- Create error class in
src/core/errors/:
// src/core/errors/custom-error.ts
import { BaseError } from './base-error';
export class CustomError extends BaseError {
constructor(message: string) {
super(message, 418, 'CustomError');
}
}- Export it in
src/core/errors/index.ts:
export { CustomError } from './custom-error';- Add to
.env.example:
NEW_CONFIG_KEY=default_value- Add to
src/core/config/index.ts:
export const NEW_CONFIG_KEY = getEnv('NEW_CONFIG_KEY', 'default');Create in src/core/utils/:
// src/core/utils/helpers.ts
export function calculateXP(level: number): number {
return level * 100;
}- Read Architecture to understand the system design
- Read Standards for coding conventions
- Read Error Handling for error management
- Read Models for data structure guidelines
- Read Responses for response system details
# Find process using port 3000
lsof -i :3000
# Kill the process
kill -9 <PID># Verify types
npm run type-check
# Check for missing dependencies
npm install- Ensure
.envfile exists in root directory - Check variable names match exactly
- Restart development server after changes
If you're experiencing CORS errors when making requests from your frontend:
Development:
- Leave
CORS_ORIGINempty or unset to allow all origins - This is suitable for local development
Production:
- Set
CORS_ORIGINto a comma-separated list of allowed origins - Example:
CORS_ORIGIN=https://yourdomain.com,https://app.yourdomain.com - Ensure
CORS_CREDENTIALS=trueif you need to send cookies or authentication headers
Common CORS Errors:
Access-Control-Allow-Originerror: Check that your frontend origin is included inCORS_ORIGINAccess-Control-Allow-Credentialserror: EnsureCORS_CREDENTIALS=truein your.envfile- Preflight requests failing: Verify that OPTIONS method is allowed (configured by default)
Testing CORS:
- Check browser console for CORS error messages
- Verify CORS headers in the response using browser DevTools Network tab
- Test with curl to verify CORS headers are present:
curl -H "Origin: https://yourdomain.com" \ -H "Access-Control-Request-Method: POST" \ -H "Access-Control-Request-Headers: Content-Type" \ -X OPTIONS \ http://localhost:3000/api/health
- Check the documentation in
/docs - Review example files in
/src/controllers,/src/services,/src/models - Ensure you're following the standards in
docs/standards.md