A microservice for managing API key permissions, built with Node.js + TypeScript, using:
- PostgreSQL for persistent storage (raw SQL only),
- NATS.io for RPC communication (request/reply),
- NATS Key-Value Store for caching permissions.
- Grant or revoke permissions for API keys (
module,action) - Check if a key has a specific permission
- List all permissions for a key
- Caching with NATS KV to reduce database load
- Fully typed with reusable helper functions
- Structured JSON logging
- Graceful error handling with typed
ErrorCode
| Technology | Purpose |
|---|---|
| Node.js + TS | Core microservice logic |
| PostgreSQL | Data storage (no ORM used) |
| NATS.io | RPC request/reply + KV store |
| NATS KV | Cache layer for fast lookup |
| Docker Compose | Local development environment |
permissions-service/
├── src/
│ ├── handlers/ # RPC Handlers: grant, revoke, check, list
│ ├── lib/ # Shared types, error codes, utils
│ ├── db/ # Raw SQL queries
│ ├── nats/ # NATS connection, KV logic
│ ├── logger.ts # Structured logger (pino or similar)
│ └── index.ts # Bootstrap and routing
├── scripts/ # CLI testing scripts
├── docker-compose.yml # For local Postgres & NATS
└── README.md
Assign permission to API key.
Request:
{
"apiKey": "abcd-1234",
"module": "trades",
"action": "create"
}
Response:
{ "status": "ok" }Remove permission from API key.
Request:
{
"apiKey": "abcd-1234",
"module": "trades",
"action": "create_manual"
}
Response:
{ "status": "ok" }Check if key has the given permission.
Request:
{ "apiKey": "abcd-1234", "module": "trades", "action": "create" }
Response:
{ "allowed": true }Get all permissions for a key.
Request:
{ "apiKey": "abcd-1234" }
Response:
{
"permissions": [
{ "module": "trades", "action": "create" },
{ "module": "trades", "action": "create_manual" }
]
}This service exposes a typed utility library for internal consumption in other services:
- Request/Response types for each topic
ErrorCodeenum- Helper functions for validation and payload building
- Cache is stored in
NATS KVunder bucketpermissions_cache. - On
checkandlist, if data is not cached:- Service fetches from PostgreSQL
- Fills KV store to avoid future DB hits
- On
grant/revoke, cache is updated immediately.
All logs are structured JSON:
{
"timestamp": "2025-08-06T08:00:00Z",
"level": "info",
"topic": "permissions.grant",
"request": { ... },
"response": { ... },
"msg": "Permission granted successfully"
}Unified error format with ErrorCode enum:
enum ErrorCode {
invalid_payload = 'invalid_payload',
db_error = 'db_error',
cache_error = 'cache_error',
apiKey_not_found = 'apiKey_not_found',
unknown = 'unknown'
}Sample error response:
{
"error": {
"code": "db_error",
"message": "Unable to execute SQL query"
}
}# Start dependencies
docker-compose up -d nats
# Install dependencies
npm install
# Build and start
npm run build && npm start npm run dev
I use local postgres if you want to use the same, you can use the following command to start the database
Grant permission:
nats request permissions.grant '{"apiKey": "abcd-1234", "module": "trades", "action": "create"}'List permissions:
nats request permissions.list '{"apiKey": "abcd-1234"}'- Retry + timeout handling on NATS requests
- Persistent KV store configuration
- Auto-reconnect and health checks
- Observability (metrics, tracing if needed)
- Unit + integration test coverage
Asliddin Nazarov
Telegram: @nemofalcon96
This project is licensed under the MIT License.