Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
5e2d332
add IDE cache folder to git ignore
oleksiybondar Feb 28, 2026
69cf063
backend-mid-specialism skeleton
oleksiybondar Feb 28, 2026
4ee74d2
freeze commend for index.mjs
oleksiybondar Feb 28, 2026
d64aca7
Update api/README.md
oleksiybondar Mar 2, 2026
9b4e837
Update api/README.md
oleksiybondar Mar 2, 2026
d341575
Update api/src/controllers/events.js
oleksiybondar Mar 2, 2026
cf36a27
remove already implemented pagination implementation
oleksiybondar Mar 2, 2026
a808974
remove already Promisse.all, by chatGPT, and use direct awaits instead
oleksiybondar Mar 2, 2026
2f263c0
remove undocumented example
oleksiybondar Mar 15, 2026
7604d64
fix issues making duplicates documented and undocumentented because o…
oleksiybondar Mar 15, 2026
0a9259f
refactor: improve MVC examples and clarify comments describing implem…
oleksiybondar Mar 15, 2026
3325509
update router according to latest changes
oleksiybondar Mar 15, 2026
28efbc0
add section explaining MVC structure and trainee responsibilities
oleksiybondar Mar 15, 2026
9876baf
add a todo statement for IDE highlight and beter look up for reviewers
oleksiybondar Mar 16, 2026
85317c8
missing autoincrement
oleksiybondar Mar 16, 2026
6604621
remove package-lock with arm64 specific dependencies
oleksiybondar Mar 16, 2026
0252d16
Merge pull request #1 from HackYourFuture-CPH/skeleton
adamblanchard Mar 17, 2026
18b1146
Update database configuration for PostgreSQL in `.env-template` and d…
oleksiybondar Apr 15, 2026
925e051
Integrate Knex.js for database migrations and seeding; update configu…
oleksiybondar Apr 15, 2026
fbc64af
Expand `api/README.md` with guidance on JavaScript module syntax and …
oleksiybondar Apr 15, 2026
3a61940
Update README files with clarification on `db:setup` usage and schema…
oleksiybondar Apr 15, 2026
fcee03c
Refactor middleware structure: split into `globalMiddlewares` and `te…
oleksiybondar Apr 15, 2026
b539631
Expand README files with guidance on extending the starter repository…
oleksiybondar Apr 15, 2026
7f98bc0
Integrate Zod for request validation and normalize inputs in event co…
oleksiybondar Apr 15, 2026
66b2007
Clarify project scope and usage of starter code in `README.md` and `a…
oleksiybondar Apr 15, 2026
dfb641c
Update import path in `knexfile.js` to use alias `#configs`
oleksiybondar Apr 15, 2026
248c17d
feat: implement postgres schema, seed data, and core event endpoints
shiprasrvstv1988-creator Apr 15, 2026
b73d7ba
docs: add ERD v1 diagram
shiprasrvstv1988-creator Apr 15, 2026
34b50ac
Update README.md
oleksiybondar Apr 16, 2026
10e8134
Address PR comments
oleksiybondar Apr 16, 2026
ac30d9d
Merge pull request #2 from HackYourFuture-CPH/improvements-from-feedback
adamblanchard Apr 16, 2026
7c9473f
feat: complete Week 2 database structure, pagination, and cart subtot…
shiprasrvstv1988-creator Apr 22, 2026
10fe422
docs: include finalized ERD v2 image
shiprasrvstv1988-creator Apr 22, 2026
6e7c862
conflicts
shiprasrvstv1988-creator May 3, 2026
8b08c33
docs: add ERD v1 diagram
shiprasrvstv1988-creator Apr 15, 2026
5d8c7bb
conflicts on rebase 2
shiprasrvstv1988-creator May 3, 2026
3660ef3
docs: include finalized ERD v2 image
shiprasrvstv1988-creator Apr 22, 2026
7a0f12f
conflicts on rebase 3
shiprasrvstv1988-creator May 3, 2026
b93e00b
feat: implement JWT auth and member cart endpoints
shiprasrvstv1988-creator May 7, 2026
5a27732
Implement checkout and orders API
shiprasrvstv1988-creator May 13, 2026
e46e3aa
Merge branch 'main' into database-week2/shipra
shiprasrvstv1988-creator May 13, 2026
b876328
Merge pull request #2 from shiprasrvstv1988-creator/database-week2/sh…
shiprasrvstv1988-creator May 13, 2026
5e486f4
Merge branch 'main' into nodejs-week1/shipra
shiprasrvstv1988-creator May 13, 2026
7b3f0e9
Merge pull request #3 from shiprasrvstv1988-creator/nodejs-week1/shipra
shiprasrvstv1988-creator May 13, 2026
4e511ea
Merge pull request #5 from shiprasrvstv1988-creator/nodejs-week2/shipra
shiprasrvstv1988-creator May 13, 2026
60beb5b
Merge pull request #6 from shiprasrvstv1988-creator/nodejs-week3/shipra
shiprasrvstv1988-creator May 13, 2026
88c900b
Fix Swagger YAML indentation
shiprasrvstv1988-creator May 13, 2026
8f97cc1
Merge pull request #7 from shiprasrvstv1988-creator/nodejs-week3/shipra
shiprasrvstv1988-creator May 13, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
.DS_STORE
.env
41 changes: 36 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ Both packages are as small as possible, but feel free to add more tools and temp

## Pre-requisites

This repository is a starter, not a complete working implementation.

- The overall structure is provided for you
- As you build project-specific features, you may need to install additional dependencies and add new environment variables
- Keep the project structure clear as you extend it, rather than trying to fit every new feature into the starter exactly as-is



## Getting started
Expand All @@ -21,12 +27,15 @@ Both packages are as small as possible, but feel free to add more tools and temp

### Setting up the Database

This project template defaults to using an SQLite database, which is stored in a generated file in the api directory. It doesn't need a separate service to run.
For the Events Startup Project, PostgreSQL is the required database.

This repository started from a general template, so the API package includes SQLite, MySQL, and PostgreSQL clients. For this course project, you should configure and use PostgreSQL.

If you wish to use MySQL instead, here's a quick way to get one set up and running using Docker:
`docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysql-root-password -e MYSQL_DATABASE=my-database -d -p 3306:3306 mysql:latest`
Here is a quick way to start PostgreSQL locally with Docker:

Whichever database you set up, connect to it using any database management tool of your choice, such as MySQL Workbench or DBeaver, to create your tables and add data.
`docker run --name events-postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=events_startup -d -p 5432:5432 postgres:16`

After the container is running, you can connect to it using a database management tool such as DBeaver or pgAdmin.
![Testing your database](./images/db_test.png)

### Setting up the API
Expand All @@ -38,7 +47,21 @@ cd api
cp .env-template .env
```

Open your .env file and configure the options as appropriate. The first thing you need to do is comment out/in the correct section in Database Configuration, depending on the database you chose in the first step. Set the variables to the correct values based on your set up.
Open your `.env` file and configure the options for PostgreSQL.

For the course project, a local PostgreSQL `.env` will usually look like this:

```env
PORT=3001

DB_CLIENT=pg
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=events_startup
DB_USE_SSL=false
```

Then you're ready to start it up:

Expand All @@ -47,6 +70,14 @@ npm install
npm run dev
```

If you are using the included demo migration and seed files, run this before `npm run dev`:

```bash
npm run db:setup
```

If you created your own PostgreSQL schema during the earlier database weeks and imported it separately, you do not need to run the included demo migrations and seeds. In that case, make sure your database schema matches the API code you are running.

You can then test the API using [Postman](https://www.postman.com/) at [http://localhost:3001/api](http://localhost:3001/api) - or just open it in your browser, for a quick peek that things are connecting successfully!

![Testing the API with Postman](./images/api_test.png)
Expand Down
35 changes: 23 additions & 12 deletions api/.env-template
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
# this is the name of the app to be shown in API documentation
APP_NAME=Backend-Mid-Specialism

# This is the port the API will be available at
PORT=3001

# -- Database configuration --
# If you set up and use a database, you'll need to use the corresponding configuration
# below as a starting point. Uncomment the section you need and customise with your
# own correct values.
#
# The default enabled configuration is for SQLite.
# For the Events Startup Project, PostgreSQL is the required database.
# This file still includes SQLite and MySQL examples because the repository
# was created from a shared starter template.

# General configuration
DB_HOST=localhost
DB_PORT=330
DB_PORT=5432
DB_USE_SSL=false # The database you use for local development most likely doesn't use SSL (encryption) but when you deploy it, you should set this to true on that environment.

# SQLite configuration - comment out/uncomment as needed
DB_CLIENT=sqlite3
DB_SQLITE_FILENAME="./src/database.sqlite3"
DB_USE_NULL_AS_DEFAULT=true
# Recommended PostgreSQL configuration for this course project
DB_CLIENT=pg
DB_USER=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=events_startup

# Optional SQLite configuration from the shared template
#DB_CLIENT=sqlite3
#DB_SQLITE_FILENAME="./src/database.sqlite3"
#DB_USE_NULL_AS_DEFAULT=true

# MySQL/PostgreSQL configuration - comment out/uncomment as needed
#DB_CLIENT=mysql2 # mysql2 or pg
# Optional MySQL configuration from the shared template
#DB_CLIENT=mysql2
#DB_USER=root
#DB_PASSWORD=root_password
#DB_DATABASE_NAME=database_name

# -- Database configuration end --

# JWT-based "Secret Key" to secure the environment
JWT_SECRET=your_super_secret_random_string_here
JWT_EXPIRES_IN=24h
7 changes: 6 additions & 1 deletion api/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
node_modules
.env
.env
# Ignore local SQLite database files
# If you intentionally want to version a database file,
# remove this rule and commit it explicitly.
src/db/*.sqlite
src/db/*.db
229 changes: 223 additions & 6 deletions api/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The API package

This package sets up a [Express](https://expressjs.com/) API server and a connection to a database (SQLite by default) using [Knex](https://knexjs.org/).
This package sets up a [Express](https://expressjs.com/) API server and a connection to a database using [Knex](https://knexjs.org/).

For development you can run the command `npm run dev` which uses `nodemon` to watch files and restarts the server when a change happens. You can find the API at [http://localhost:3001/api](http://localhost:3001/api).

Expand All @@ -14,14 +14,126 @@ You can set environment variables in the `.env` file or in the Render.com enviro

When you start a fresh project, check out `.env-template` to get started. Create a file called `.env` and copy the contents of the template as a starting point (or just run `cp .env-template .env`). You should comment in/out the sections you need, and add any additional configuration as necessary.

## Scope clarifications

This README explains how the starter API is structured. It does not replace the project contract, PRD, or weekly plan.

- Some routes in the starter exist mainly as examples of controller/router/model structure
- Placeholder admin-style CRUD routes are illustrative and may be optional depending on the course scope
- Core project scope should still be taken from the program requirements and contract

When designing your own database and API, a few practical conventions are worth keeping in mind:

- Avoid reserved database table names such as `user` and `order`; use safer names if needed
- Route params such as `itemId` should identify the record named by the route, for example a cart line item rather than a catalog event
- If the project contract specifies a route shape, follow that contract even if another route design could also work

Design hint:

If your cart API uses a route such as `/api/cart/items/{itemId}`, that usually implies a single unique identifier for the cart line item itself. If your database instead identifies a cart line by a composite key such as `(cart_id, line_no)`, the route shape would typically need to reflect that design as well, for example `/api/carts/{cartId}/lines/{lineNo}`.

## Node & Packages

This project uses modern JavaScript modules throughout the codebase.

- Use `import` / `export` syntax in project files
- If you see older Node.js examples online using `require()` and `module.exports`, that is CommonJS syntax and not the style used in this repository
- This skeleton intentionally includes only the dependencies and environment variables needed for the base starter setup
- As you implement new features, you are expected to install additional packages and extend `.env` with any new variables you need
- Common examples for auth work are `bcryptjs` for password hashing, `jsonwebtoken` for JWT handling, and a `JWT_SECRET` environment variable for signing tokens

The goal is to keep the project structure and separation of concerns consistent while still allowing you to extend the implementation as your project grows.

## Input validation

Backend input validation is also expected in this project for request bodies, path params, and query params.

This starter now includes [Zod](https://zod.dev/) as a simple schema-based example for input validation. It was chosen intentionally because the schema definitions stay compact, readable, and close to the actual data shape.

The included examples live in `src/schemas/events.js` as named schemas for request input.

- Use it as a reference if you want a schema-based approach
- Zod schemas can validate and normalize input in one step with `.parse(...)`
- If `.parse(...)` fails, Zod throws a `ZodError`, which this starter maps to a `400` JSON response
- You may still use manual validation if that fits your learning process better
- You may also choose a different validation library if you have a good reason

The important part is that incoming input is validated before it is used by controllers or models.

### Example

```js
import { EventInput, EventListQuery } from "#schemas/events.js";

export async function postEvent(req, res, next) {
try {
const eventInput = EventInput.parse(req.body);
// continue with validated and normalized input
} catch (error) {
next(error);
}
}
```

`EventInput.parse(req.body)` returns a validated object ready to use in the controller.

For query params, the same pattern can be used:

```js
const { page, pageSize } = EventListQuery.parse(req.query);
```

If you prefer manual validation, the wiring is similar: parse the input near the start of the controller, throw a `400`-type error when a value is invalid, and pass only normalized values deeper into the model layer.

For comparison, `src/schemas/events.js` also includes commented manual alternatives that throw on invalid input and return normalized values on success.

## Middleware placement

This project uses middleware in more than one place, depending on what the middleware is supposed to do.

- Use `globalMiddlewares` for middleware that should run before all routes
- Use `terminalMiddlewares` for fallback and error handling after routes
- Use router-level or route-level middleware for protected endpoints or feature-specific behavior

Examples:

- logging middleware can be global
- an optional auth extractor can be global if it does not block public routes
- `requireAuth` should usually be attached on protected routers or routes
- 404 and error handlers belong in the terminal middleware group

In general, if a middleware would block public endpoints such as `/api/events`, do not add it as a global middleware.

### Example

Router-level middleware protects all routes in that router:

```js
import express from "express";
import { requireAuth } from "#middlewares/auth.js";

const ordersRouter = express.Router();

ordersRouter.use(requireAuth);

ordersRouter.get("/", getOrders);
ordersRouter.get("/:id", getOrderById);
```

Route-level middleware protects only one specific route:

```js
eventsRouter.get("/", getEvents); // public
eventsRouter.post("/", requireAuth, postEvent); // protected
```

## Database clients

The package comes installed with an SQLite, MySQL, and PostgreSQL client. Here's a quick suggestion for use cases:
1. SQLite for quick, simple file-based storage
2. MySQL for more advanced data storage (requires you to run a database service)
3. PostgreSQL, similar to MySQL and used on our recommended hosting platform Render.com
The package comes installed with SQLite, MySQL, and PostgreSQL clients because it is based on a shared template.

You can decide which client to use by changing the `DB_CLIENT` environment variable. See `.env-template` for more info.
For the Events Startup Project, keep the course-specific database setup in the [main README](../README.md), including the required `DB_CLIENT=pg` setting, local Docker example, and example `.env` values.

See `.env-template` for the full list of available configuration variables supported by this package.

## Advanced database management

Expand All @@ -33,6 +145,111 @@ Combined, these two techniques make it very easy to experiment with changes to y

It also makes it possible to share temporary schema changes with others during Pull Request testing.

### Running migrations and seeds

This project includes helper scripts for running Knex migrations and seeds:

- `npm run db:migrate` – runs all pending migrations
- `npm run db:seed` – runs all seed files
- `npm run db:setup` – runs migrations and then seeds (fresh setup)
- `npm run db:migrate:make <name>` – creates a new migration file using the standard Knex CLI
- `npm run db:seed:make <name>` – creates a new seed file using the standard Knex CLI

If you are using the included example schema in this repository, run `npm run db:setup` before starting the API.

If you built your own PostgreSQL schema during the database weeks and imported it separately, the included demo migration and seed files are optional. In that case, your own schema becomes the source of truth, and it must match the API code you are running.

You can use either the npm scripts above or the Knex CLI directly from inside the `api` directory.

Example:

```bash
npm run db:migrate:make create_user_table
```

Direct Knex CLI example:

```bash
npx knex migrate:make create_user_table
```

Both commands create a timestamped migration file in `src/db/migrations`.

### Creating a new migration or seed

Migrations and seeds are considered advanced project tooling in this repository, so the README only documents the local project conventions and the basic commands. For more complete usage patterns, refer to the official Knex documentation.

Create a migration:

```bash
npm run db:migrate:make create_user_table
```

Create a seed:

```bash
npm run db:seed:make 002_users
```

New migration files are created in `src/db/migrations`, and new seed files are created in `src/db/seeds`.

For migrations, Knex expects an `up()` function for applying the schema change and a `down()` function for rolling it back:

```js
export async function up(knex) {
await knex.schema.createTable("user", (table) => {
table.increments("id").primary();
table.string("email").notNullable().unique();
});
}

export async function down(knex) {
await knex.schema.dropTableIfExists("user");
}
```

See the [Knex migration documentation](https://knexjs.org/guide/migrations.html) and [Knex seed documentation](https://knexjs.org/guide/migrations.html#seed-files) for more details.

The `db:setup` command is useful when:

- You are starting the project for the first time
- You want to reset your local database
- You are running the demo version of the API

### Project structure

This skeleton includes a simple **MVC-style structure**:

- **Routers** define API endpoints
- **Controllers** handle HTTP request/response logic
- **Models** contain database access logic

MVC is introduced intentionally in this project, even if it has not been deeply covered as a formal pattern during the course.

The reason is to simulate a more realistic backend project environment:
you may join a codebase where the overall structure was already chosen by a more experienced developer, and your task is to understand it, follow it, and continue building within it.

At the same time, this project keeps the implementation aligned with the JavaScript level of the course:

- modules and exported functions
- no class-based OOP requirement
- no advanced framework abstractions
- SQL and Knex used in a direct and readable way

So while the **project structure** is slightly more professional and opinionated than a blank beginner project, the **coding style itself** remains intentionally simple and compatible with the modular JavaScript you have learned.

Knex migrations and seeds are also included as an **advanced database management technique** often used in professional backend projects.

For your tasks, assignments and learning purposes, you are expected to:

- design your own database schema
- write your own SQL queries
- understand how your tables are structured
- continue working within the provided MVC structure

You should not rely blindly on the provided migrations if your assignment requires you to build your own schema.
If needed, modify or remove the existing migrations and seeds to match your database assignments.

## Deploying

> Last tested: 2025-07-08
Expand Down
4 changes: 4 additions & 0 deletions api/knexfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import "dotenv/config";
import { createKnexConfig } from "#configs/knex-config.js";

export default createKnexConfig();
Loading