Skip to content

jcscott2015/node-postgres-api

Repository files navigation

node-postgres-api

A RESTful API server built with Node.js, Express, TypeScript, and PostgreSQL. Features OAuth 2.0 authentication (client credentials + authorization code flows), JWT-based access control, real-time WebSocket support, and structured request validation.

node-postgres-spa is the front end project for this service.

Tech Stack

Layer Technology
Runtime Node.js (ESM)
Framework Express 5
Language TypeScript
Database PostgreSQL 16
Auth OAuth 2.0 (@node-oauth/oauth2-server) + JWT
Validation Zod
WebSockets ws
Logging Winston + Morgan
Testing Vitest + Supertest
Container Docker + Docker Compose

Project Structure

src/
├── app.ts                  # Express app setup, routes, middleware wiring
├── server.ts               # HTTP server + WebSocket initialization
├── db.ts                   # PostgreSQL connection pool
├── oauth2Model.ts          # OAuth 2.0 model (token generation, validation)
├── socket.ts               # WebSocket server and event handlers
├── controllers/
│   ├── authController.ts   # Token exchange + authentication verification
│   ├── logoutController.ts # Logout handler
│   └── userController.ts   # CRUD operations for users
├── middleware/
│   ├── authGuard.ts        # JWT bearer token enforcement
│   ├── catchAsync.ts       # Async error wrapper
│   ├── errorHandler.ts     # Global error handler
│   ├── logger.ts           # Winston logger instance
│   └── validate.ts         # Zod schema request validator
├── schemas/
│   └── userSchema.ts       # Zod schemas + inferred TypeScript types
└── types/
    ├── db.ts               # Database row types
    └── express.d.ts        # Express type augmentations
init-db/
└── init.sql                # Schema + seed data for PostgreSQL

Prerequisites

Getting Started

With Docker (recommended)

Starts both the API server and a PostgreSQL instance. The database schema is initialized automatically via init-db/init.sql.

docker compose up --build

The API will be available at http://localhost:5000.

Local Development

  1. Install dependencies

    pnpm install
  2. Configure environment

    Create a .env file in the project root:

    PORT=5000
    JWT_SECRET=demo-client-secret
    DB_USER=postgres
    DB_HOST=localhost
    DB_NAME=api_db
    DB_PASSWORD=secretpassword
    DB_PORT=5432
  3. Start a local PostgreSQL instance (or use the Docker Compose db service only):

    docker compose up db
  4. Run the dev server

    pnpm dev

Scripts

Command Description
pnpm dev Start dev server with hot reload (ts-node-dev)
pnpm build Compile TypeScript to dist/
pnpm start Run compiled output (dist/server.js)
pnpm test Run tests with Vitest

API Reference

All protected endpoints use an HttpOnly accessToken cookie issued by /oauth/token.

Authentication

POST /oauth/token

Exchange client credentials for an HttpOnly session cookie.

curl -X POST http://localhost:5000/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=demo-client-id&client_secret=demo-client-secret"

Response

{
  "tokenType": "Bearer",
  "accessTokenExpiresAt": "2026-01-01T00:00:00.000Z",
  "scope": []
}

The JWT is set as an HttpOnly cookie in the Set-Cookie response header.

GET /oauth/session

Returns the current authenticated session context from the cookie.

POST /oauth/logout

Invalidates the current session.

GET /secure-rest

Verifies token validity and returns the authenticated context.

Users

GET /api/users

Returns all users ordered by ID.

GET /api/users/:id

Returns a single user by numeric ID.

POST /api/users

Creates a new user. Also broadcasts a user_created WebSocket event to connected clients.

Request body

{
  "name": "Jane Doe",
  "email": "jane@example.com"
}

PUT /api/users/:id

Replaces an existing user record.

Request body — same shape as POST /api/users.

WebSocket

Connect to ws://localhost:5000/ws. Browser clients can authenticate via the HttpOnly cookie. The server also accepts token subprotocol authentication for non-browser clients. The server supports the following events:

Event Description
subscribe Subscribe to server-pushed events
ping Heartbeat keepalive
create_user Create a user via WebSocket
get_user Fetch a single user
list_users Fetch all users
update_user Replace a user record
patch_user Partially update a user
delete_user Delete a user

Send a JSON message with event and payload fields:

{
  "event": "list_users",
  "payload": {}
}

Database Schema

CREATE TABLE users (
    id         SERIAL PRIMARY KEY,
    name       VARCHAR(100) NOT NULL,
    email      VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE oauth_clients (
    id            VARCHAR(255) PRIMARY KEY,
    client_secret VARCHAR(255) NOT NULL,
    grants        TEXT[] NOT NULL,
    redirect_uris TEXT[] NOT NULL
);

A seed OAuth client (demo-client-id / demo-client-secret) is inserted automatically on first run.

Running Tests

pnpm test

Tests use Vitest and Supertest. Integration tests exercise the HTTP layer directly against a running database.

Environment Variables

Variable Default Description
PORT 5000 Port the HTTP server listens on
JWT_SECRET demo-client-secret Secret used to sign and verify JWTs
DB_USER PostgreSQL username
DB_HOST PostgreSQL hostname
DB_NAME PostgreSQL database name
DB_PASSWORD PostgreSQL password
DB_PORT 5432 PostgreSQL port

Note: Do not use the default JWT_SECRET in production. Set a strong, randomly generated secret.

About

A RESTful API server built with Node.js, Express, TypeScript, and PostgreSQL. Features OAuth 2.0 authentication (client credentials + authorization code flows), JWT-based access control, real-time WebSocket support, and structured request validation.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors