Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
cafc420
chore: add `.nvmrc` file
leoarena Apr 1, 2026
e7771bf
chore: add `express` package and `.gitignore`
leoarena Apr 1, 2026
0bea8c4
chore: add typescript dev dependencies
leoarena Apr 1, 2026
df067b0
chore: add `jest` dev dependencies
leoarena Apr 1, 2026
9f02b87
chore: add `prettier` dev dependencies and configuration
leoarena Apr 1, 2026
7c1846c
chore: add docker-compose with mongodb
leoarena Apr 1, 2026
8564448
chore: add mongodb dependencies
leoarena Apr 1, 2026
d5e6700
feat: add TimeSeries model with unique seriesId
leoarena Apr 1, 2026
8626a15
feat: add mongodb connection configuration
leoarena Apr 1, 2026
ae33dc6
feat: add express server with health check endpoint
leoarena Apr 2, 2026
74d75cb
feat: add zod validation for time series input
leoarena Apr 2, 2026
fce08cd
chore: add PORT to .env.example
leoarena Apr 3, 2026
9c39c67
refactor: replace manual interfaces with InferSchemaType for time ser…
leoarena Apr 3, 2026
7066baf
refactor: coerce timestamp string to Date in time series dto
leoarena Apr 3, 2026
09cf3f3
feat: implement createTimeSeries service with unit tests
leoarena Apr 3, 2026
12c683c
feat: add generic Zod validation middleware for request bodies
leoarena Apr 3, 2026
3473b62
feat: add AppError class and global error handling middleware
leoarena Apr 3, 2026
0f9b216
feat: update service and tests to use AppError for duplicate entries
leoarena Apr 3, 2026
6dd59d0
feat: implement global error handler and 404 middleware
leoarena Apr 3, 2026
04af2a6
chore: add supertest dev dependencies for HTTP tests
leoarena Apr 3, 2026
250c3cf
feat: implement time series POST endpoint with controller, routes and…
leoarena Apr 3, 2026
60ec60f
feat: implement GET endpoint to count total stored series with supert…
leoarena Apr 3, 2026
c77d86f
refactor: split request validation into body and params validators
leoarena Apr 4, 2026
b65b27d
feat: implement GET endpoint to retrieve a full time series with supe…
leoarena Apr 4, 2026
c1858a4
feat: implement deleteBySeriesId service with unit tests
leoarena Apr 4, 2026
c633dfa
test: add unit tests for countTimeSeries service
leoarena Apr 4, 2026
f871193
test: add unit tests for getBySeriesId service
leoarena Apr 4, 2026
cbd47c3
feat: implement DELETE endpoint to delete a time series with supertes…
leoarena Apr 4, 2026
031d7a0
refactor: serialize time series responses and hide Mongo fields
leoarena Apr 4, 2026
0a2a1bf
refactor: rename `TimeSeries.ts` to `time-series.model.ts`
leoarena Apr 4, 2026
2761b4d
feat: implement getMetricsBySeriesId service with unit tests
leoarena Apr 4, 2026
f434c30
feat: implement GET endpoint to retrieve metrics about the time serie…
leoarena Apr 4, 2026
714aa77
refactor: rename `AppError.ts` to `app-error.ts`
leoarena Apr 4, 2026
e4919fc
refactor: rename `time-series.controller.spec.ts` to `time-series.rou…
leoarena Apr 4, 2026
4f56f4f
refactor: rename `errorHandler.ts` to `error-handler.ts`
leoarena Apr 4, 2026
4f79cb3
refactor: standardize API contract to snake_case
leoarena Apr 4, 2026
2fabed0
refactor: remove unused points.timestamp index
leoarena Apr 4, 2026
9abcddf
build: add `build` and `start` scripts
leoarena Apr 5, 2026
5604f35
chore: add database setup scripts
leoarena Apr 5, 2026
db29c8a
build: exclude test files with a dedicated tsconfig
leoarena Apr 5, 2026
af4f672
feat: validate unique timestamps within the same time series
leoarena Apr 5, 2026
d7acbc1
docs: add project README
leoarena Apr 5, 2026
f3e77ee
test: update route spec descriptions to series_id
leoarena Apr 5, 2026
670f43d
test: silence ts-jest warnings and update Jest transform config
leoarena Apr 5, 2026
ba53b98
docs: add build and start instructions to README
leoarena Apr 5, 2026
b624b11
feat: enforce payload size and points limits for time series creation
leoarena Apr 5, 2026
7ae1c89
refactor: simplify POST /api/series response payload
leoarena Apr 5, 2026
5d19198
docs: update README with payload limits and POST response contract
leoarena Apr 5, 2026
1b89acd
refactor: centralize environment variable handling
leoarena Apr 5, 2026
87d868f
docs: add deployed API URL to README
leoarena Apr 5, 2026
4fca850
docs: clarify README commands must be run from the project directory
leoarena Apr 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"ms-vscode.vscode-typescript-next"
]
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
3 changes: 3 additions & 0 deletions back-end-challenge-v2-solution/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MONGODB_URI=mongodb://localhost:27017
MONGODB_DATABASE=dynamox
PORT=3000
3 changes: 3 additions & 0 deletions back-end-challenge-v2-solution/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
.env
dist
1 change: 1 addition & 0 deletions back-end-challenge-v2-solution/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v24.14.1
1 change: 1 addition & 0 deletions back-end-challenge-v2-solution/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
1 change: 1 addition & 0 deletions back-end-challenge-v2-solution/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
282 changes: 282 additions & 0 deletions back-end-challenge-v2-solution/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
# Back-end Challenge V2 Solution

Implementation of `back-end-challenge-v2.md` using Node.js, Express, MongoDB, and Jest.

**All commands in this README must be run from the back-end-challenge-v2-solution directory.**

## Overview

This API allows clients to:

- create a time series
- retrieve a full time series
- retrieve time series metrics
- delete a time series
- count how many time series are stored

The public API contract is standardized in `snake_case`.

## Stack

- Node.js
- TypeScript
- Express
- MongoDB with Mongoose
- Zod for validation
- Jest + Supertest for tests
- Docker for local MongoDB

## Deployed API

The application is also deployed as a bonus feature.

Base URL:

```text
https://developer-challenges-n2rp.onrender.com
```

Health check:

```text
https://developer-challenges-n2rp.onrender.com/health
```

Note:

- the deployed version uses free-tier cloud services
- response times may vary due to shared infrastructure, cold starts, and network latency between the application and the managed database
- local execution is a more reliable reference for evaluating the application itself than the free-tier deployment environment

## Prerequisites

Make sure the following tools are installed on your machine:

- `nvm` or another Node.js version manager
- Node.js
- npm
- Docker
- Docker Compose

This repository includes a [.nvmrc](.nvmrc) file. If you use `nvm`, run:

```bash
nvm use
```

If the required Node.js version is not installed yet, run:

```bash
nvm install
nvm use
```

## Implemented User Stories

- store a raw time series
- retrieve a full time series
- retrieve metrics for a time series
- delete a time series
- retrieve the total number of stored time series

## Endpoints

### `POST /api/series`

Creates a new time series.

Example payload:

```json
{
"series_id": "S1",
"unit": "C",
"points": [
{
"timestamp": "2024-01-01T00:00:00.000Z",
"value": 10
}
]
}
```

Example response:

```json
{
"series_id": "S1",
"created_at": "2026-04-04T16:16:11.845Z"
}
```

### `GET /api/series/:series_id`

Returns the full time series.

### `GET /api/series/:series_id/metrics`

Returns metrics for the time series.

Example response:

```json
{
"series_id": "S1",
"unit": "C",
"total_points": 3,
"min_value": 10,
"max_value": 30,
"average_value": 20,
"first_timestamp": "2024-01-01T00:00:00.000Z",
"last_timestamp": "2024-01-03T00:00:00.000Z"
}
```

### `DELETE /api/series/:series_id`

Deletes a time series.

Expected response:

- `204 No Content`

### `GET /api/series/count`

Returns the total number of stored time series.

Example response:

```json
{
"total_series": 5
}
```

### `GET /health`

Returns the basic application and MongoDB connection status.

## Validation Rules

- `series_id` must be a non-empty string
- `unit` must be a non-empty string
- `points` must contain at least one item
- `points` must contain at most `2000` items
- `timestamp` must use ISO 8601 format
- timestamps must be unique within the same time series
- `series_id` must be unique per series
- JSON request payloads above `512kb` are rejected with `413 Payload Too Large`

Notes:

- repeated timestamps are allowed across different time series
- series uniqueness is enforced by `seriesId`

## How to Run

### 1. Install the correct Node.js version

If you use `nvm`:

```bash
nvm install
nvm use
```

### 2. Install dependencies

```bash
npm install
```

### 3. Configure the environment

Copy `.env.example` to `.env` and adjust values if needed.

Available variables:

```env
MONGODB_URI=mongodb://localhost:27017
MONGODB_DATABASE=dynamox
PORT=3000
```

### 4. Start MongoDB

```bash
npm run db:start
```

### 5. Start the application in development mode

```bash
npm run dev
```

By default, the API will be available at:

```text
http://localhost:3000
```

### 6. Build and start the application

Compile the project:

```bash
npm run build
```

Start the compiled server:

```bash
npm start
```

## Running Tests

```bash
npm test
```

Or in watch mode:

```bash
npm run test:watch
```

## Project Structure

```text
src/
config/
controllers/
dto/
errors/
middlewares/
models/
routes/
services/
```

## Implementation Decisions

- the HTTP contract uses `snake_case`
- the internal domain and model representation use `camelCase`
- response serialization is handled in the DTO layer
- `seriesId` has a unique index in MongoDB
- metrics are calculated in memory after retrieving the full series
- request size and number of points are bounded to keep ingestion latency under control

## Test Coverage

- invalid payload validation
- duplicate timestamp validation within the same time series
- maximum points validation for time series creation
- time series creation
- time series count
- full time series retrieval by `series_id`
- time series metrics retrieval
- time series deletion
- `400`, `404`, and `413` scenarios
- service tests and HTTP tests with Supertest
10 changes: 10 additions & 0 deletions back-end-challenge-v2-solution/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
mongodb:
image: mongo:7.0
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db

volumes:
mongodb_data:
22 changes: 22 additions & 0 deletions back-end-challenge-v2-solution/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Config } from "jest";

const config: Config = {
testEnvironment: "node",
extensionsToTreatAsEsm: [".ts"],
transform: {
"^.+\\.ts$": [
"ts-jest",
{
useESM: true,
diagnostics: {
ignoreCodes: [151002],
},
},
],
},
moduleNameMapper: {
"^(\\.{1,2}/.*)\\.js$": "$1",
},
};

export default config;
Loading