Skip to content

Commit 87d9561

Browse files
committed
first commit
0 parents  commit 87d9561

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+8508
-0
lines changed

Diff for: .github/workflows/test.yml

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Run Tests
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
lint:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Setup Node.js
16+
uses: actions/setup-node@v4
17+
with:
18+
node-version: '20'
19+
20+
- name: Install pnpm
21+
uses: pnpm/action-setup@v2
22+
with:
23+
version: 8
24+
run_install: false
25+
26+
- name: Install dependencies
27+
run: pnpm install --frozen-lockfile
28+
29+
- name: Run lint
30+
run: pnpm lint
31+
32+
test:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
37+
- name: Setup Node.js
38+
uses: actions/setup-node@v4
39+
with:
40+
node-version: '20'
41+
42+
- name: Install pnpm
43+
uses: pnpm/action-setup@v2
44+
with:
45+
version: 8
46+
run_install: false
47+
48+
- name: Install dependencies
49+
run: pnpm install --frozen-lockfile
50+
51+
- name: Run tests
52+
run: pnpm test b vgn

Diff for: .gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build
2+
node_modules
3+
.env

Diff for: CMD.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## Initalise project
2+
pnpm init
3+
4+
npx tsc --init
5+
6+
## Install dependencies
7+
pnpm add drizzle-orm postgres pino pino-pretty http-status-codes argon2 @fastify/secure-session @fastify/cookie fastify prom-client env-schema @fastify/swagger @fastify/swagger-ui pg-error-enum slugify fastify-type-provider-zod zod drizzle-zod
8+
9+
10+
11+
pnpm add -D drizzle-kit
12+
13+
## Install dev dependencies
14+
pnpm add drizzle-kit typescript tsx json-schema-to-ts @types/node vitest testcontainers @testcontainers/postgresql vitest @faker-js/faker -D
15+
16+
## Generate session key file
17+
npx --yes @fastify/secure-session > session-key

Diff for: Dockerfile

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Use an official Node.js runtime as a parent image
2+
FROM node:20-alpine
3+
4+
# Set the working directory
5+
WORKDIR /app
6+
7+
RUN apk add --no-cache \
8+
libc6-compat \
9+
build-base
10+
11+
# Copy package.json and pnpm-lock.yaml
12+
COPY package.json pnpm-lock.yaml ./
13+
14+
# Install pnpm
15+
RUN npm install -g pnpm
16+
17+
# Install dependencies
18+
RUN pnpm install
19+
20+
# Copy the rest of the application code
21+
COPY . .
22+
23+
# Build the application
24+
RUN pnpm build
25+
26+
# Expose the port the app runs on
27+
EXPOSE 4000
28+
29+
CMD ["node", "build/src/main.js"]

Diff for: README.md

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Build a REST API like a senior developer
2+
3+
## What you will learn
4+
* Basic Principals of REST APIs
5+
* How to structure your applications
6+
* How to test REST APIs
7+
* How to use Docker for local development
8+
* How to use Drizzle for database interactions
9+
* How to use JSON Schema for API design
10+
* Basic TypeScript
11+
* How to capture and visualise metrics
12+
13+
14+
## Modules
15+
### User
16+
* Functional style
17+
* json-schema
18+
19+
### Job
20+
* Object-oriented
21+
* Dependency injection
22+
* json-schema
23+
24+
## Job application
25+
* Object-oriented
26+
* Dependency injection
27+
* Zod schema
28+
29+
## Features
30+
* Authentication
31+
* User management
32+
* Job management
33+
* Metrics
34+
35+
36+
37+
## Technology
38+
* Node.js
39+
* Fastify
40+
* Drizzle
41+
* Postgres
42+
* TypeScript
43+
* Pino
44+
* Prometheus
45+
* Grafana
46+
47+
## What you'll need
48+
* A code editor - VSCode/Cursor
49+
* A HTTP client - Postman
50+
* Node.js installed
51+
* Docker installed (Optional) or a Postgres instance
52+
* SQL client - [TablePlus](https://tableplus.com) (Optional)
53+
54+
## Options for Postgres
55+
* Docker (Recommended)
56+
* Local Postgres instance
57+
* DBngin (local) https://dbngin.com/
58+
* Neon (Cloud) https://neon.tech/
59+
* Supabase (Cloud) https://supabase.com/
60+
61+
## Part 1 - Design
62+
* Database design
63+
* API design
64+
65+
## Part 2 - Setup Docker (Optional)
66+
* Postgres
67+
* API
68+
* Dozzle (Logs)
69+
70+
## Part 3 - Project setup
71+
* Install dependencies
72+
* Install dev dependencies
73+
* Initalise project
74+
* Setup database
75+
* Setup logging
76+
* Configure server
77+
78+
## Part 4 - User module
79+
* Register user
80+
* Login
81+
* Get user
82+
* Testing
83+
84+
## Part 5 - Job module
85+
* Create job
86+
* Get job
87+
* Update job
88+
* Delete job
89+
* Testing
90+
91+
## Part 6 - Job application
92+
* Create job application
93+
* Get job applications for a job
94+
* Update job application status
95+
* Delete job application
96+
* Testing
97+
98+
## Part 7 - Metrics
99+
* Setup Prometheus
100+
* Capture default metrics
101+
* Add a histogram for all requests
102+
* Add a custom metric for database calls
103+
* Visualise metrics with Grafana
104+
105+
## Challenges
106+
1. Verify the user's account via email
107+
2. Add an OAuth flow with an identity provider like Google or GitHub
108+
3. Add multi-tenancy
109+
110+
## Need help?
111+
Discord: https://discord.gg/4ae2Esm6P7
112+
Twitter: https://twitter.com/tomdoes_tech
113+
Bluesky: https://tomdoestech.bsky.social

Diff for: WT.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Building a REST API
2+
- [ ] Tech stack (TypeScript, Node.js, Fastify, PostgreSQL, Drizzle)
3+
- [ ] Routing & app structure
4+
- [ ] File structure
5+
- [ ] Developer setup (Docker, Docker Compose & Dozzle)
6+
- [ ] Server (Fastify)
7+
- [ ] Logging
8+
- [ ] Config & environment variables
9+
- [ ] Database (PostgreSQL + Drizzle)
10+
- [ ] Schemas & validation
11+
- [ ] Error handling
12+
- [ ] Auth (Session)
13+
- [ ] Observability (Prometheus, Grafana)
14+
- [ ] Testing (Vitest)
15+
- [ ] Documentation (Swagger)
16+
- [ ] CI/CD (GitHub Actions)
17+
- [ ] Deployment ([Railway](https://railway.com/), [Fly.io](https://fly.io/))
18+
19+
# Where to get help?
20+
Discord: https://discord.gg/4ae2Esm6P7
21+
X: https://x.com/tomdoes_tech

Diff for: diagram.svg

+4
Loading

Diff for: docker-compose.dev.yml

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
services:
3+
db:
4+
container_name: db
5+
image: postgres:13
6+
ports:
7+
- "5432:5432"
8+
volumes:
9+
- pgdata:/var/lib/postgresql/data
10+
environment:
11+
- POSTGRES_USER=postgres
12+
- POSTGRES_PASSWORD=password
13+
- POSTGRES_DB=postgres
14+
15+
api:
16+
environment:
17+
- DATABASE_URL=postgres://postgres:password@db:5432/postgres
18+
command: >
19+
sh -c "
20+
pnpm db:migrate &&
21+
npx nodemon -w /app/migrations -e sql -x 'pnpm db:migrate' &
22+
pnpm dev
23+
"
24+
volumes:
25+
- .:/app
26+
- /app/node_modules
27+
depends_on:
28+
- db
29+
30+
volumes:
31+
pgdata:

Diff for: docker-compose.yml

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
services:
2+
api:
3+
container_name: api
4+
build:
5+
context: .
6+
dockerfile: Dockerfile
7+
ports:
8+
- "4000:4000"
9+
healthcheck:
10+
test: ["CMD", "curl", "-f", "http://localhost:4000/healthcheck"]
11+
interval: 1m30s
12+
timeout: 30s
13+
retries: 5
14+
start_period: 30s
15+
volumes:
16+
- ./session-keys:/app/session-keys
17+
18+
dozzle:
19+
container_name: dozzle
20+
image: amir20/dozzle:latest
21+
volumes:
22+
- /var/run/docker.sock:/var/run/docker.sock
23+
ports:
24+
- 8080:8080
25+
26+
prometheus:
27+
image: prom/prometheus
28+
container_name: prometheus
29+
command:
30+
- '--config.file=/etc/prometheus/prometheus.yml'
31+
ports:
32+
- 9090:9090
33+
restart: unless-stopped
34+
volumes:
35+
- ./prometheus:/etc/prometheus
36+
- prom_data:/prometheus
37+
38+
grafana:
39+
image: grafana/grafana
40+
container_name: grafana
41+
ports:
42+
- 3000:3000
43+
restart: unless-stopped
44+
environment:
45+
- GF_SECURITY_ADMIN_USER=admin
46+
- GF_SECURITY_ADMIN_PASSWORD=password
47+
volumes:
48+
- ./grafana/provisioning:/etc/grafana/provisioning
49+
- ./grafana/dashboards:/var/lib/grafana/dashboards
50+
- grafana-storage:/var/lib/grafana
51+
52+
volumes:
53+
prom_data:
54+
grafana-storage:

Diff for: drizzle.config.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { defineConfig } from "drizzle-kit";
2+
import { config } from "./src/config";
3+
4+
export default defineConfig({
5+
schema: "./src/db/schema.ts",
6+
out: "./migrations",
7+
dialect: "postgresql", // 'postgresql' | 'mysql' | 'sqlite'
8+
dbCredentials: {
9+
url: config.DATABASE_URL ?? "",
10+
},
11+
});

Diff for: eslint.config.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import globals from "globals";
2+
import pluginJs from "@eslint/js";
3+
import tseslint from "typescript-eslint";
4+
5+
6+
/** @type {import('eslint').Linter.Config[]} */
7+
export default [
8+
{files: ["**/*.{js,mjs,cjs,ts}"]},
9+
{languageOptions: { globals: globals.browser }},
10+
pluginJs.configs.recommended,
11+
...tseslint.configs.recommended,
12+
];

0 commit comments

Comments
 (0)