Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Changesets

This project uses [Changesets](https://github.com/changesets/changesets) to version releases and generate changelogs.

## Adding a changeset

When your PR includes a user-facing change, run:

```sh
pnpm changeset
```

Commit the generated file under `.changeset/` with your PR.

## Releasing

On push to `main`, the Release workflow opens or updates a **Version Packages** PR. Merging that PR bumps the version, updates `CHANGELOG.md`, and creates a GitHub Release (this package is private, so nothing is published to npm).
18 changes: 18 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "dillonstreator/template-node-express" }
],
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
"privatePackages": {
"version": true,
"tag": true
}
}
5 changes: 5 additions & 0 deletions .changeset/pnpm-security-upgrades.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"template-node-express": minor
---

Switch the package manager from Yarn to pnpm, upgrade dependencies to resolve known security vulnerabilities, and align OpenTelemetry APIs with the current SDK.
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
pull_request:
push:
branches:
- main

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build
run: pnpm build

- name: Test
run: pnpm test
41 changes: 41 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Release

on:
push:
branches:
- main

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write
pull-requests: write

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Create release PR or GitHub Release
uses: changesets/action@v1
with:
title: Version Packages
commit: Version Packages
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24 changes: 12 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
FROM node as builder
FROM node:22-bookworm AS builder

WORKDIR /usr/src/app

COPY package.json yarn.lock ./
RUN corepack enable

RUN yarn install --frozen-lockfile
COPY package.json pnpm-lock.yaml ./

RUN pnpm install --frozen-lockfile

COPY . .

RUN yarn build
RUN pnpm build && pnpm prune --prod

FROM node:slim
FROM node:22-bookworm-slim

ENV NODE_ENV production
ENV NODE_ENV=production
USER node

WORKDIR /usr/src/app

COPY package.json yarn.lock ./

RUN yarn install --production --frozen-lockfile

COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder --chown=node:node /usr/src/app/package.json ./
COPY --from=builder --chown=node:node /usr/src/app/node_modules ./node_modules
COPY --from=builder --chown=node:node /usr/src/app/dist ./dist

ENV PORT 3000
ENV PORT=3000
EXPOSE $PORT

CMD [ "node", "dist/index.js" ]
17 changes: 13 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A minimal production-ready node HTTP server with [`Express`](https://expressjs.c
✅ Async error forwarding to default error handler with [`express-async-errors`](https://github.com/davidbanham/express-async-errors) \
✅ Structured logging with [`pino`](https://github.com/pinojs/pino) \
✅ Rich request logging middleware including request id, trace id, context propagation, and more \
✅ Testing with [`jest`](https://github.com/jestjs/jest), [`supertest`](https://github.com/ladjs/supertest), and [`fetch-mock`](https://github.com/wheresrhys/fetch-mock) \
✅ Testing with [`jest`](https://github.com/jestjs/jest), [`supertest`](https://github.com/forwardemail/supertest), and [`fetch-mock`](https://github.com/wheresrhys/fetch-mock) \
✅ [`helmet`](https://github.com/helmetjs/helmet) & [`compression`](https://github.com/expressjs/compression)

[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/KwYYFA?referralCode=ToZEjF)
Expand All @@ -23,9 +23,10 @@ git clone https://github.com/dillonstreator/template-node-express

cd template-node-express

yarn install
corepack enable
pnpm install

yarn dev
pnpm dev
```

## Configuration
Expand All @@ -38,4 +39,12 @@ Open Telemetry is disabled by default but can be enabled by setting the `OTEL_EN

By default, the trace exporter is set to standard output. This can be overridden by setting `OTEL_EXPORTER_OTLP_ENDPOINT`.

Start the `jaegertracing/all-in-one` container with `docker-compose up` and set `OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318` to collect logs in jaeger. Docker compose will expose jaeger at http://localhost:16686
Start the `jaegertracing/all-in-one` container with `docker-compose up` and set `OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318` to collect logs in jaeger. Docker compose will expose jaeger at http://localhost:16686

## Releasing

This template uses [Changesets](https://github.com/changesets/changesets) for versioning and changelogs (not npm publish).

1. For user-facing changes, run `pnpm changeset` and commit the file it creates.
2. After changesets land on `main`, the Release workflow opens a **Version Packages** PR.
3. Merging that PR bumps the version, updates `CHANGELOG.md`, and creates a GitHub Release.
75 changes: 48 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,48 +1,69 @@
{
"name": "template-node-express",
"version": "1.0.0",
"private": true,
"main": "dist/index.js",
"license": "MIT",
"packageManager": "pnpm@10.12.4",
"engines": {
"node": ">=20"
},
"scripts": {
"dev": "nodemon src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "jest src"
"test": "jest src",
"changeset": "changeset",
"version-packages": "changeset version",
"release": "changeset publish"
},
"devDependencies": {
"@babel/core": "^7.23.3",
"@babel/preset-env": "^7.23.3",
"@babel/preset-typescript": "^7.23.3",
"@babel/core": "^7.29.6",
"@babel/preset-env": "^7.29.6",
"@babel/preset-typescript": "^7.28.5",
"@changesets/changelog-github": "^0.7.0",
"@changesets/cli": "^2.31.1",
"@jest/globals": "^29.7.0",
"@types/compression": "^1.7.5",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.8",
"@types/node": "^20.9.2",
"@types/compression": "^1.8.1",
"@types/express": "^4.17.23",
"@types/jest": "^29.5.14",
"@types/node": "^20.19.9",
"@types/request-ip": "^0.0.41",
"@types/supertest": "^2.0.16",
"@types/supertest": "^6.0.3",
"babel-jest": "^29.7.0",
"fetch-mock": "^12.6.0",
"jest": "^29.7.0",
"nodemon": "^3.0.1",
"supertest": "^6.3.3",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"nodemon": "^3.1.10",
"supertest": "^7.1.4",
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
},
"dependencies": {
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.45.1",
"@opentelemetry/resources": "^1.18.1",
"@opentelemetry/sdk-node": "^0.218.0",
"@opentelemetry/sdk-trace-base": "^1.18.1",
"@opentelemetry/semantic-conventions": "^1.18.1",
"compression": "^1.7.4",
"express": "^4.20.0",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/auto-instrumentations-node": "^0.78.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.220.0",
"@opentelemetry/resources": "^2.9.0",
"@opentelemetry/sdk-node": "^0.220.0",
"@opentelemetry/sdk-trace-base": "^2.9.0",
"@opentelemetry/semantic-conventions": "^1.43.0",
"compression": "^1.8.1",
"express": "^4.22.2",
"express-async-errors": "^3.1.1",
"express-validator": "^7.0.1",
"fetch-mock": "^9.11.0",
"helmet": "^7.1.0",
"http-graceful-shutdown": "^3.1.13",
"pino": "^8.16.2",
"express-validator": "^7.3.2",
"helmet": "^8.1.0",
"http-graceful-shutdown": "^3.1.16",
"pino": "^9.7.0",
"request-ip": "^3.3.0"
},
"pnpm": {
"onlyBuiltDependencies": [
"protobufjs"
],
"overrides": {
"js-yaml": "^3.15.0",
"form-data": "^4.0.6",
"protobufjs": "^7.6.3",
"qs": "^6.15.2"
}
}
}
Loading
Loading