Thank you for your interest in contributing to PolyAPI! This guide will help you understand how to add new modules and contribute to the project.
- Architecture Overview
- Adding a New Module
- Module Requirements
- Docker Networking
- Code Style
- Testing
- PR Checklist
- CLA Requirement
PolyAPI uses a microservices architecture where:
- Gateway (Python FastAPI): The only externally accessible service. Routes requests to modules.
- Modules: Individual services written in any language. Communicate exclusively via JSON over HTTP.
- Network: All inter-module communication happens through an internal Docker network (
polyapi-net).
Client → Gateway (port 8000) → Modules (internal network)
Adding a new module to PolyAPI involves several steps:
Create a new directory under modules/ with your module name:
modules/
└── mymodule/
├── main.go # or main.rs, main.ts, etc.
├── handler.go # or equivalent
├── Dockerfile
└── go.mod # or package.json, Cargo.toml, etc.
Your module must implement the JSON contract as defined in CONTRACT.md:
- Accept request envelopes with
request_id,module,version, andpayload - Return response envelopes with
status,data, anderrorfields - Implement proper error handling with error codes
- All communication must be HTTP + JSON (no shared memory, databases, or direct imports)
Add a health check endpoint:
GET /health
Response:
{
"status": "ok",
"module": "mymodule",
"version": "1.0.0"
}Every module must have a Dockerfile. The Dockerfile should:
- Expose only the internal port (not externally accessible)
- Use environment variables for configuration
- Be built as part of the orchestrator docker-compose
Example:
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /mymodule
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /mymodule .
EXPOSE 8082
CMD ["./mymodule"]Update the gateway configuration to include your module:
-
Register service in
gateway/config.py:SERVICES = { "mymodule": { "name": "mymodule", "language": "Go", # or your language "port": "8082", "url": os.getenv("MYMODULE_URL", "http://mymodule:8082"), "description": "Description of what your module does", "version": "1.0.0", }, }
-
Add route in
gateway/router/routes.py: Create a new endpoint that proxies to your module.
Add your module to docker-compose.yml:
services:
gateway:
# ... existing config
environment:
- MYMODULE_URL=http://mymodule:8082
mymodule:
build:
context: ./modules/mymodule
dockerfile: Dockerfile
container_name: polyapi-mymodule
environment:
- PORT=8082
volumes:
- mymodule-data:/app/data
networks:
- polyapi-net
restart: unless-stopped
volumes:
mymodule-data:
name: polyapi-mymodule-dataCreate documentation in docs/modules/:
docs/modules/
└── mymodule.mdInclude:
- Module description
- API reference (endpoints, request/response schemas)
- Examples
- Error codes
- Implementation notes
- JSON contract compliance (request/response envelopes)
- Health endpoint (
GET /health) - Proper error handling with contract error codes
- Environment-based configuration (no hardcoded URLs/ports)
- Dockerfile for containerization
- Named volume for data persistence (e.g.,
mymodule-data:/app/data) - Module documentation
- Registered in
gateway/config.py - Route added in
gateway/router/routes.py - Service added to
docker-compose.yml - Communicates via HTTP + JSON over internal Docker network
- Unit tests
- Input validation
- Logging
- Graceful shutdown
- Metrics endpoint
- OpenAPI/Swagger documentation
- Rate limiting
All modules communicate through the polyapi-net Docker network. This network:
- Is defined in
docker-compose.yml - Is accessible only to containers running in the same compose project
- Is NOT exposed externally (only the gateway has exposed ports)
Modules MUST NOT:
- Expose ports externally (remove
portsfrom docker-compose for modules) - Connect to external services directly (go through the gateway)
- Share databases or filesystems with other modules
Modules MUST:
- Listen on the port specified in their
PORTenvironment variable - Respond to
/healthfor health checks - Return JSON contract-compliant responses
- Follow Effective Go conventions
- Use
go vetand check for warnings - Add GoDoc comments on all exported functions
- Run
golintif available
Example:
// HandleSort processes the sort request.
func HandleSort(w http.ResponseWriter, r *http.Request) {
// implementation
}- Follow PEP 8 style guide
- Use type hints where appropriate
- Add docstrings to all functions
- Run
ruff checkfor linting
Example:
async def sort_items(request: Request) -> ResponseEnvelope:
"""Sort items via the sort module.
Args:
request: The incoming HTTP request with sort payload.
Returns:
ResponseEnvelope with sorted items or error.
"""
pass- Use meaningful variable and function names
- Keep functions focused and small
- Write comments that explain why, not what
- Handle errors gracefully (no raw exceptions to clients)
# Build and start services
make up
# Run test suite
make test
# View logs
make logsTest through the gateway (the only externally accessible service):
# Test via gateway
curl -X POST http://localhost:8000/your-endpoint \
-H "Content-Type: application/json" \
-d '{"payload": {}}'Before submitting a pull request, ensure:
- Code follows the style guide for the relevant language
- All tests pass (
make test) - Linting passes (
make lint) - Documentation is updated
- Module works end-to-end with
make up - No hardcoded URLs or ports
- Error responses follow the contract format
- Commit messages are clear and descriptive
- Service registered in
gateway/config.py - Route added in
gateway/router/routes.py - Module added to
docker-compose.yml - Module Dockerfile exists and is properly configured
- Module does NOT expose external ports (internal network only)
- You have read and agree to the CLA
Before your contribution can be accepted, you must sign the Contributor License Agreement (CLA). By submitting a pull request, patch, or other form of contribution to the Project, you agree to be bound by the terms of the CLA.
- Open an issue for bugs or feature requests
- Join discussions in the repository
- Check existing issues and PRs before creating new ones
We look forward to your contributions!