DrugRegistry exposes public read APIs for drugs and pharmacies.
Create a .env file from .env.example, set a real POSTGRES_PASSWORD, then start the stack:
docker compose up --build -dCompose startup order:
dbstarts Postgres and waits forpg_isready.migrationsruns an idempotent EF migration bundle.apistarts only after migrations complete successfully.
The migration bundle is safe to run on every docker compose up; it only applies pending migrations.
Required/important environment variables:
POSTGRES_PASSWORD: required, no default.POSTGRES_USER: defaultdrugregistry.POSTGRES_DB: defaultdrugdb.POSTGRES_DATA_PATH: default../files/postgres-data.API_PORT: default8080.CORS_ALLOWED_ORIGINS: comma-separated browser origins allowed to call the API. Empty means no browser CORS origins are allowed.FORWARDED_HEADERS_KNOWN_PROXIES: comma-separated proxy IP addresses allowed to supplyX-Forwarded-*headers. Leave empty when the API is exposed directly.FORWARDED_HEADERS_KNOWN_NETWORKS: comma-separated proxy CIDR networks allowed to supplyX-Forwarded-*headers. Leave empty when the API is exposed directly.DATA_INGESTION_RUN_BOOTSTRAP_ON_STARTUP: defaultfalse. Set totrueonly when you intentionally want startup to trigger scraping/seeding for empty tables.
The API requires ConnectionStrings:Database at startup. For local dotnet run or IDE launch, store it in user secrets instead of committing a developer password:
dotnet user-secrets set --project DrugRegistry.API "ConnectionStrings:Database" "Host=localhost;Port=5432;Database=drugdb;Username=drugregistry;Password=<your-local-password>"- Scalar API docs:
GET /docs - OpenAPI JSON:
GET /openapi/v2.json,GET /openapi/v1.json - Liveness:
GET /health/live - Readiness:
GET /health/ready
Swagger UI and Swashbuckle are not used.
V2 is available under /api/v2 and is the current public API.
GET /api/v2/drugs- Query params:
page,size,query, repeatableid. - Examples:
/api/v2/drugs?page=0&size=10,/api/v2/drugs?query=paracetamol,/api/v2/drugs?id={guid1}&id={guid2}.
- Query params:
GET /api/v2/drugs/{id}GET /api/v2/drugs/ean/{ean}
GET /api/v2/pharmacies- Query params:
page,size,municipality,place,query,lon,lat, repeatableid. - Examples:
/api/v2/pharmacies?query=zegin,/api/v2/pharmacies?lon=21.433&lat=41.998,/api/v2/pharmacies?id={guid1}&id={guid2}.
- Query params:
GET /api/v2/pharmacies/{id}GET /api/v2/pharmacies/municipalitiesGET /api/v2/pharmacies/municipalities/{municipality}/places
Collection endpoints return data, totalCount, page, and size. Invalid inputs return RFC 7807 Problem Details.
Request limits:
page:0..500, default0.size:1..100, default10.query:2..200trimmed characters.id: at most50repeated ids per request.municipality: at most100trimmed characters.place: at most100trimmed characters.ean: at most32trimmed characters.lon: finite number in-180..180.lat: finite number in-90..90.lonandlatmust be provided together.
Rate limits are per client IP with no queueing:
- Public API endpoints:
120requests per minute. - Scalar/OpenAPI docs:
30requests per minute. - Health endpoints:
60requests per minute.
Cache TTLs:
- List/search endpoints:
2 minutes. - Detail endpoints by id/EAN:
10 minutes. - Municipality/place lookup endpoints:
30 minutes. - Health endpoints and errors are not cached.
Fuzzy search remains in memory. Query, page, and size limits are enforced before fuzzy search runs.
Existing V1 paths under /api/* remain available for compatibility, but they are deprecated in favor of /api/v2/*. V1 is also rate-limited and validates the same public limits where applicable.