Add Node API gateway standard endpoints
This commit is contained in:
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
OS_ROOT=https://blackroad.systems
|
||||||
|
SERVICE_BASE_URL=https://api.blackroad.systems
|
||||||
|
CORE_BASE_URL=https://core.blackroad.systems
|
||||||
|
OPERATOR_BASE_URL=https://operator.blackroad.systems
|
||||||
|
LOG_LEVEL=info
|
||||||
|
NODE_ENV=development
|
||||||
|
PORT=8080
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -205,3 +205,9 @@ cython_debug/
|
|||||||
marimo/_static/
|
marimo/_static/
|
||||||
marimo/_lsp/
|
marimo/_lsp/
|
||||||
__marimo__/
|
__marimo__/
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
coverage/
|
||||||
|
npm-debug.log*
|
||||||
|
|||||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Build stage
|
||||||
|
FROM node:18-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Runtime stage
|
||||||
|
FROM node:18-alpine
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/package*.json ./
|
||||||
|
RUN npm install --omit=dev
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
ENV PORT=8080
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["npm", "start"]
|
||||||
95
README.md
95
README.md
@@ -1,86 +1,61 @@
|
|||||||
# BlackRoad OS — Public API Gateway
|
# BlackRoad OS – Public API
|
||||||
|
|
||||||
FastAPI-powered public gateway that fronts Core and Agents services with versioned routing, thin proxying, and API key enforcement.
|
Public API gateway for the BlackRoad Operating System. This service exposes common health/info endpoints and versioned API routes that coordinate with core BlackRoad services.
|
||||||
|
|
||||||
## What it does
|
## Endpoints
|
||||||
|
|
||||||
- Exposes `/health` and `/version` for liveness and build metadata.
|
- `GET /health` – Liveness check
|
||||||
- Adds `/v1` routes with API key authentication and consistent error wrapping.
|
- `GET /info` – Service metadata
|
||||||
- Proxies requests to upstreams:
|
- `GET /version` – Version info
|
||||||
- `/v1/core/*` → `CORE_API_URL`
|
- `GET /debug/env` – Safe subset of environment values
|
||||||
- `/v1/agents/*` → `AGENTS_API_URL`
|
- `GET /v1/ping` – Example API endpoint
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
Environment variables are loaded through `app.config.Settings`.
|
|
||||||
|
|
||||||
| Variable | Required | Description |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `NODE_ENV` | No (default `development`) | Deployment environment label. |
|
|
||||||
| `PUBLIC_API_URL` | Yes (non-dev) | External URL for this gateway. |
|
|
||||||
| `CORE_API_URL` | Yes (non-dev) | Upstream Core backend base URL. |
|
|
||||||
| `AGENTS_API_URL` | No | Upstream Agents API base URL. |
|
|
||||||
| `API_KEYS` | Yes (non-dev) | Comma-separated list of API keys authorized for `/v1` routes. |
|
|
||||||
| `PUBLIC_API_KEY` | Optional | Single API key value if not using `API_KEYS`. |
|
|
||||||
| `LOG_LEVEL` | No | Application log level (default `info`). |
|
|
||||||
| `REQUEST_TIMEOUT_MS` | No | Upstream request timeout in milliseconds (default `10000`). |
|
|
||||||
| `GIT_COMMIT` / `RAILWAY_GIT_COMMIT_SHA` | No | Commit SHA used for `/version`. |
|
|
||||||
| `BUILD_TIME` | No | Build timestamp used for `/version`. |
|
|
||||||
|
|
||||||
## Running locally
|
## Running locally
|
||||||
|
|
||||||
1. Install dependencies:
|
1. Install dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Export a sample API key (either `API_KEYS` or `PUBLIC_API_KEY` works):
|
2. Start the development server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export PUBLIC_API_KEY=local-dev-key
|
npm run dev
|
||||||
export CORE_API_URL=http://localhost:9000 # point to your Core service
|
|
||||||
export AGENTS_API_URL=http://localhost:9100 # optional
|
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Start the gateway:
|
The API listens on `http://localhost:8080` by default.
|
||||||
|
|
||||||
```bash
|
## Build and start
|
||||||
uvicorn app.main:app --reload --port 8000
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example requests
|
|
||||||
|
|
||||||
Health and version (no API key required):
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:8000/health
|
npm run build
|
||||||
curl http://localhost:8000/version
|
npm start
|
||||||
curl http://localhost:8000/v1/health
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Proxying upstreams (API key required):
|
## Environment variables
|
||||||
|
|
||||||
|
See `.env.example` for defaults. Key values:
|
||||||
|
|
||||||
|
- `OS_ROOT` – Base URL for the BlackRoad OS
|
||||||
|
- `SERVICE_BASE_URL` – External URL for this public API
|
||||||
|
- `CORE_BASE_URL` – Core service base URL
|
||||||
|
- `OPERATOR_BASE_URL` – Operator service base URL
|
||||||
|
- `LOG_LEVEL` – Logging verbosity
|
||||||
|
- `PORT` – Port to bind (default `8080`)
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Run the test suite with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Ping Core
|
npm test
|
||||||
curl -H "x-api-key: local-dev-key" http://localhost:8000/v1/core/ping
|
|
||||||
|
|
||||||
# Forward any Core path
|
|
||||||
curl -X POST \
|
|
||||||
-H "x-api-key: local-dev-key" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"hello": "world"}' \
|
|
||||||
"http://localhost:8000/v1/core/some/path"
|
|
||||||
|
|
||||||
# Forward any Agents path
|
|
||||||
curl -H "x-api-key: local-dev-key" "http://localhost:8000/v1/agents/demo"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deployment
|
## Deployment (Railway)
|
||||||
|
|
||||||
- **Railway project**: `blackroad-core`
|
Railway uses `railway.json`:
|
||||||
- **Service name**: `public-api`
|
|
||||||
- **Build**: `pip install -r requirements.txt`
|
|
||||||
- **Start**: `uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}`
|
|
||||||
|
|
||||||
GitHub Actions workflow `.github/workflows/api-deploy.yaml` deploys to Railway on `dev`, `staging`, and `main` branches and runs health checks against `/health` and `/v1/health` after each deploy.
|
- Build: `npm install && npm run build`
|
||||||
|
- Start: `npm start`
|
||||||
|
- Healthcheck: `/health` on port `8080`
|
||||||
|
|||||||
10
jest.config.js
Normal file
10
jest.config.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: "ts-jest",
|
||||||
|
testEnvironment: "node",
|
||||||
|
roots: ["<rootDir>/tests"],
|
||||||
|
moduleFileExtensions: ["ts", "js", "json"],
|
||||||
|
moduleNameMapper: {
|
||||||
|
"^@/(.*)": "<rootDir>/src/$1",
|
||||||
|
},
|
||||||
|
};
|
||||||
5517
package-lock.json
generated
Normal file
5517
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
package.json
Normal file
28
package.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "blackroad-os-public-api",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "BlackRoad OS – Public API gateway service",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "NODE_ENV=development ts-node-dev src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "NODE_ENV=production node dist/index.js",
|
||||||
|
"test": "NODE_ENV=test jest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.19.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/node": "^20.12.12",
|
||||||
|
"@types/supertest": "^2.0.16",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"supertest": "^6.3.4",
|
||||||
|
"ts-jest": "^29.1.1",
|
||||||
|
"ts-node-dev": "^2.0.0",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
26
railway.json
26
railway.json
@@ -1,22 +1,10 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://railway.app/railway.schema.json",
|
"build": "npm install && npm run build",
|
||||||
"name": "public-api",
|
"start": "npm start",
|
||||||
"project": "blackroad-core",
|
|
||||||
"service": {
|
"service": {
|
||||||
"name": "public-api",
|
"port": 8080,
|
||||||
"envVar": "PORT",
|
"healthcheck": {
|
||||||
"startCommand": "uvicorn app.main:app --host 0.0.0.0 --port ${PORT:-8000}",
|
"path": "/health"
|
||||||
"buildCommand": "pip install -r requirements.txt",
|
}
|
||||||
"healthcheckPath": "/health"
|
}
|
||||||
},
|
|
||||||
"variables": [
|
|
||||||
"NODE_ENV",
|
|
||||||
"PUBLIC_API_URL",
|
|
||||||
"CORE_API_URL",
|
|
||||||
"AGENTS_API_URL",
|
|
||||||
"API_KEYS",
|
|
||||||
"PUBLIC_API_KEY",
|
|
||||||
"LOG_LEVEL",
|
|
||||||
"REQUEST_TIMEOUT_MS"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
19
src/config/serviceConfig.ts
Normal file
19
src/config/serviceConfig.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export const SERVICE_ID = "api";
|
||||||
|
export const SERVICE_NAME = "BlackRoad OS – Public API";
|
||||||
|
export const SERVICE_BASE_URL =
|
||||||
|
process.env.SERVICE_BASE_URL || "https://api.blackroad.systems";
|
||||||
|
export const OS_ROOT = process.env.OS_ROOT || "https://blackroad.systems";
|
||||||
|
|
||||||
|
export const CORE_BASE_URL =
|
||||||
|
process.env.CORE_BASE_URL || "https://core.blackroad.systems";
|
||||||
|
export const OPERATOR_BASE_URL =
|
||||||
|
process.env.OPERATOR_BASE_URL || "https://operator.blackroad.systems";
|
||||||
|
|
||||||
|
export const serviceConfig = {
|
||||||
|
SERVICE_ID,
|
||||||
|
SERVICE_NAME,
|
||||||
|
SERVICE_BASE_URL,
|
||||||
|
OS_ROOT,
|
||||||
|
CORE_BASE_URL,
|
||||||
|
OPERATOR_BASE_URL,
|
||||||
|
};
|
||||||
39
src/index.ts
Normal file
39
src/index.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import express from "express";
|
||||||
|
import cors from "cors";
|
||||||
|
import healthRouter from "./routes/health";
|
||||||
|
import infoRouter from "./routes/info";
|
||||||
|
import versionRouter from "./routes/version";
|
||||||
|
import debugEnvRouter from "./routes/debugEnv";
|
||||||
|
import v1PingRouter from "./routes/v1/ping";
|
||||||
|
import { loggingMiddleware } from "./middleware/logging";
|
||||||
|
import { errorHandler } from "./middleware/errorHandler";
|
||||||
|
import { SERVICE_ID } from "./config/serviceConfig";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(loggingMiddleware);
|
||||||
|
|
||||||
|
app.use(healthRouter);
|
||||||
|
app.use(infoRouter);
|
||||||
|
app.use(versionRouter);
|
||||||
|
app.use(debugEnvRouter);
|
||||||
|
app.use("/v1", v1PingRouter);
|
||||||
|
|
||||||
|
app.use(errorHandler);
|
||||||
|
|
||||||
|
const PORT = process.env.PORT ? Number(process.env.PORT) : 8080;
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "test") {
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify({
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
message: `Service ${SERVICE_ID} listening on port ${PORT}`,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
||||||
22
src/middleware/errorHandler.ts
Normal file
22
src/middleware/errorHandler.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { SERVICE_ID } from "../config/serviceConfig";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const errorHandler = (
|
||||||
|
err: Error,
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): void => {
|
||||||
|
const status = res.statusCode >= 400 ? res.statusCode : 500;
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== "test") {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(status).json({
|
||||||
|
ok: false,
|
||||||
|
error: err.message || "Internal Server Error",
|
||||||
|
service: SERVICE_ID,
|
||||||
|
});
|
||||||
|
};
|
||||||
28
src/middleware/logging.ts
Normal file
28
src/middleware/logging.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { SERVICE_ID } from "../config/serviceConfig";
|
||||||
|
|
||||||
|
export const loggingMiddleware = (
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
): void => {
|
||||||
|
const start = process.hrtime.bigint();
|
||||||
|
|
||||||
|
res.on("finish", () => {
|
||||||
|
const end = process.hrtime.bigint();
|
||||||
|
const durationMs = Number(end - start) / 1_000_000;
|
||||||
|
|
||||||
|
const logEntry = {
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
method: req.method,
|
||||||
|
path: req.originalUrl || req.url,
|
||||||
|
status: res.statusCode,
|
||||||
|
duration_ms: Number(durationMs.toFixed(3)),
|
||||||
|
service_id: SERVICE_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(JSON.stringify(logEntry));
|
||||||
|
});
|
||||||
|
|
||||||
|
next();
|
||||||
|
};
|
||||||
25
src/routes/debugEnv.ts
Normal file
25
src/routes/debugEnv.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import {
|
||||||
|
SERVICE_ID,
|
||||||
|
OS_ROOT,
|
||||||
|
CORE_BASE_URL,
|
||||||
|
OPERATOR_BASE_URL,
|
||||||
|
} from "../config/serviceConfig";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/debug/env", (req: Request, res: Response) => {
|
||||||
|
res.json({
|
||||||
|
ok: true,
|
||||||
|
service: SERVICE_ID,
|
||||||
|
env: {
|
||||||
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
|
OS_ROOT,
|
||||||
|
LOG_LEVEL: process.env.LOG_LEVEL,
|
||||||
|
CORE_BASE_URL,
|
||||||
|
OPERATOR_BASE_URL,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
14
src/routes/health.ts
Normal file
14
src/routes/health.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import { SERVICE_ID } from "../config/serviceConfig";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/health", (req: Request, res: Response) => {
|
||||||
|
res.json({
|
||||||
|
ok: true,
|
||||||
|
service: SERVICE_ID,
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
17
src/routes/info.ts
Normal file
17
src/routes/info.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import packageInfo from "../../package.json";
|
||||||
|
import { SERVICE_ID, SERVICE_NAME } from "../config/serviceConfig";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/info", (req: Request, res: Response) => {
|
||||||
|
res.json({
|
||||||
|
name: SERVICE_NAME,
|
||||||
|
id: SERVICE_ID,
|
||||||
|
version: packageInfo.version,
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
env: process.env.NODE_ENV || "development",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
15
src/routes/v1/ping.ts
Normal file
15
src/routes/v1/ping.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import { SERVICE_ID, SERVICE_NAME } from "../../config/serviceConfig";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/ping", (req: Request, res: Response) => {
|
||||||
|
res.json({
|
||||||
|
ok: true,
|
||||||
|
api: SERVICE_NAME,
|
||||||
|
service: SERVICE_ID,
|
||||||
|
ts: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
14
src/routes/version.ts
Normal file
14
src/routes/version.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import packageInfo from "../../package.json";
|
||||||
|
import { SERVICE_ID } from "../config/serviceConfig";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
router.get("/version", (req: Request, res: Response) => {
|
||||||
|
res.json({
|
||||||
|
version: packageInfo.version,
|
||||||
|
service: SERVICE_ID,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
37
src/services.ts
Normal file
37
src/services.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
export type ServiceDescriptor = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
baseUrl: string;
|
||||||
|
healthEndpoint: string;
|
||||||
|
infoEndpoint: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const apiService: ServiceDescriptor = {
|
||||||
|
id: "api",
|
||||||
|
name: "BlackRoad OS – Public API",
|
||||||
|
baseUrl: process.env.SERVICE_BASE_URL || "https://api.blackroad.systems",
|
||||||
|
healthEndpoint: "/health",
|
||||||
|
infoEndpoint: "/info",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const coreService: ServiceDescriptor = {
|
||||||
|
id: "core",
|
||||||
|
name: "BlackRoad OS – Core",
|
||||||
|
baseUrl: process.env.CORE_BASE_URL || "https://core.blackroad.systems",
|
||||||
|
healthEndpoint: "/health",
|
||||||
|
infoEndpoint: "/info",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const operatorService: ServiceDescriptor = {
|
||||||
|
id: "operator",
|
||||||
|
name: "BlackRoad OS – Operator",
|
||||||
|
baseUrl: process.env.OPERATOR_BASE_URL || "https://operator.blackroad.systems",
|
||||||
|
healthEndpoint: "/health",
|
||||||
|
infoEndpoint: "/info",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const servicesRegistry = {
|
||||||
|
api: apiService,
|
||||||
|
core: coreService,
|
||||||
|
operator: operatorService,
|
||||||
|
};
|
||||||
12
tests/health.test.ts
Normal file
12
tests/health.test.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import request from "supertest";
|
||||||
|
import app from "../src/index";
|
||||||
|
|
||||||
|
describe("GET /health", () => {
|
||||||
|
it("returns ok status and service id", async () => {
|
||||||
|
const response = await request(app).get("/health");
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body).toHaveProperty("ok", true);
|
||||||
|
expect(response.body).toHaveProperty("service", "api");
|
||||||
|
});
|
||||||
|
});
|
||||||
16
tests/info.test.ts
Normal file
16
tests/info.test.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import request from "supertest";
|
||||||
|
import app from "../src/index";
|
||||||
|
import packageInfo from "../package.json";
|
||||||
|
|
||||||
|
describe("GET /info", () => {
|
||||||
|
it("returns service metadata", async () => {
|
||||||
|
const response = await request(app).get("/info");
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body).toMatchObject({
|
||||||
|
id: "api",
|
||||||
|
name: "BlackRoad OS – Public API",
|
||||||
|
version: packageInfo.version,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
12
tests/ping.test.ts
Normal file
12
tests/ping.test.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import request from "supertest";
|
||||||
|
import app from "../src/index";
|
||||||
|
|
||||||
|
describe("GET /v1/ping", () => {
|
||||||
|
it("returns ok response", async () => {
|
||||||
|
const response = await request(app).get("/v1/ping");
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(response.body).toHaveProperty("ok", true);
|
||||||
|
expect(response.body).toHaveProperty("service", "api");
|
||||||
|
});
|
||||||
|
});
|
||||||
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "commonjs",
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user