A RESTful HTTP server built with Node.js, Express, and PostgreSQL. Chirpy is a Twitter-like microblogging API where users can create short posts ("chirps"), authenticate with JWTs, and manage their accounts.
- Runtime: Node.js (ESM)
- Framework: Express
- Database: PostgreSQL via Drizzle ORM
- Auth: JWT (access tokens) + opaque refresh tokens
- Password hashing: Argon2
- Node.js 18+
- PostgreSQL
npm installCreate a .env file in the project root:
DB_URL=postgres://<user>:<password>@localhost:5432/<dbname>?sslmode=disable
PLATFORM=dev
JWT_SECRET=<your_secret>
POLKA_KEY=<your_polka_api_key>
PLATFORM=devenables the admin reset endpoint. Do not use in production.
npm run build && npm startThe server listens on http://localhost:8080.
Protected endpoints require a JWT access token in the Authorization header:
Authorization: Bearer <access_token>
Endpoints using API key authentication require:
Authorization: ApiKey <api_key>
Create a new user.
Request body:
{
"email": "user@example.com",
"password": "password123"
}Response 201:
{
"id": "uuid",
"createdAt": "timestamp",
"updatedAt": "timestamp",
"email": "user@example.com",
"isChirpyRed": false
}Update the authenticated user's email and/or password.
Auth: Bearer token required
Request body:
{
"email": "new@example.com",
"password": "newpassword"
}Response 200:
{
"id": "uuid",
"createdAt": "timestamp",
"updatedAt": "timestamp",
"email": "new@example.com",
"isChirpyRed": false
}Authenticate a user and receive access and refresh tokens.
Request body:
{
"email": "user@example.com",
"password": "password123"
}Response 200:
{
"id": "uuid",
"createdAt": "timestamp",
"updatedAt": "timestamp",
"email": "user@example.com",
"token": "<jwt_access_token>",
"refreshToken": "<refresh_token>",
"isChirpyRed": false
}Response 401: Invalid credentials.
Exchange a valid refresh token for a new JWT access token.
Auth: Bearer token required (refresh token)
Response 200:
{
"token": "<new_jwt_access_token>"
}Response 401: Refresh token is invalid, expired, or revoked.
Revoke a refresh token.
Auth: Bearer token required (refresh token)
Response 204: No content.
Create a new chirp. Chirps are limited to 140 characters. Profane words (kerfuffle, sharbert, fornax) are automatically replaced with ****.
Auth: Bearer token required
Request body:
{
"body": "Hello world!"
}Response 201:
{
"id": "uuid",
"createdAt": "timestamp",
"updatedAt": "timestamp",
"body": "Hello world!",
"userId": "uuid"
}Response 400: Missing body or chirp exceeds 140 characters.
Retrieve all chirps.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
authorId |
string | Filter chirps by user ID |
sort |
string | Sort order: asc (default) or desc |
Response 200: Array of chirp objects.
Retrieve a single chirp by ID.
Response 200:
{
"id": "uuid",
"createdAt": "timestamp",
"updatedAt": "timestamp",
"body": "Hello world!",
"userId": "uuid"
}Response 404: Chirp not found.
Delete a chirp. Only the author can delete their own chirps.
Auth: Bearer token required
Response 204: No content.
Response 401: Not authenticated.
Response 403: Authenticated user is not the author.
Webhook endpoint for the Polka payment provider. Upgrades a user to Chirpy Red on a user.upgraded event.
Auth: ApiKey required
Request body:
{
"event": "user.upgraded",
"data": {
"userId": "uuid"
}
}Response 204: No content (success or unrecognised event).
Response 401: Invalid API key.
Response 404: User not found.
These endpoints are only available when
PLATFORM=dev.
Health check endpoint.
Response 200: OK
Returns the number of times the /app file server has been hit.
Response 200: Plain text hit count.
Resets the file server hit counter and deletes all users from the database.
Response 200: OK hits were reset