A Twitter-like social media backend built with Go and PostgreSQL.
Chirpy API is a production-style REST backend inspired by Twitter. The project was built as part of the Boot.dev Backend Path and expanded into a complete social media API featuring authentication, authorization, refresh tokens, webhooks, and PostgreSQL persistence.
The project demonstrates modern backend engineering practices in Go, including:
- JWT authentication and authorization
- Refresh token sessions
- Argon2 password hashing
- Type-safe database queries with SQLC
- Database migrations with Goose
- Webhook integrations
- Automated releases with GitHub Actions RESTful API design
- User registration
- User login
- Argon2 password hashing
- JWT access tokens
- Refresh token authentication
- Refresh token revocation
- Protected endpoints
- Resource ownership authorization
- Create chirps
- Retrieve all chirps
- Retrieve a chirp by ID
- Delete chirps
- Filter chirps by author
- Sort chirps by creation date
- Profanity filtering
- Register users
- Update email and password
- Chirpy Red memberships
- Health check endpoint
- Metrics endpoint
- Development reset endpoint
- Polka webhook integration
- API key authentication for webhooks
| Category | Technology |
|---|---|
| Language | Go |
| Database | PostgreSQL |
| Authentication | JWT |
| Password Hashing | Argon2id |
| Query Generation | SQLC |
| Database Migrations | Goose |
| CI/CD | GitHub Actions |
| API Style | REST |
Client
│
▼
REST API (Go)
│
├── Authentication (JWT + Refresh Tokens)
├── Business Logic
├── Webhooks
└── PostgreSQL
.
├── assets
│ └── logo.png
├── internal
│ ├── auth
│ │ ├── apikey.go
│ │ ├── bearer.go
│ │ ├── jwt.go
│ │ ├── passwords.go
│ │ └── refresh.go
│ └── database
│ ├── chirps.sql.go
│ ├── refresh_tokens.sql.go
│ ├── users.sql.go
│ └── models.go
├── sql
│ ├── queries
│ └── schema
├── main.go
├── go.mod
├── sqlc.yaml
└── README.md
- Go 1.26+
- PostgreSQL
- SQLC
- Goose
Create a .env file:
DB_URL=postgres://postgres:postgres@localhost:5432/chirpy?sslmode=disable
JWT_SECRET=your-super-secret-jwt-key
POLKA_KEY=f271c81ff7084ee5b99a5091b42d486e
PLATFORM=devImportant
Never commit your real .env file. Create a .env.example file and commit that instead.
git clone https://github.com/shubh1855/chirpy-api.git
cd chirpy-apigo mod downloadCREATE DATABASE chirpy;goose postgres "$DB_URL" upsqlc generatego run .The server will be available at:
http://localhost:8080
go test ./...| Method | Endpoint | Description |
|---|---|---|
| POST | /api/users |
Register a user |
| POST | /api/login |
Login |
| POST | /api/refresh |
Refresh access token |
| POST | /api/revoke |
Revoke refresh token |
| PUT | /api/users |
Update user |
| POST | /api/chirps |
Create chirp |
| GET | /api/chirps |
Get all chirps |
| GET | /api/chirps/{chirpID} |
Get chirp by ID |
| DELETE | /api/chirps/{chirpID} |
Delete chirp |
| POST | /api/polka/webhooks |
Process webhook |
| GET | /api/healthz |
Health check |
| GET | /admin/metrics |
Metrics |
| POST | /admin/reset |
Reset database (dev only) |
POST /api/usersRequest:
{
"email": "[email protected]",
"password": "super-secret-password"
}Response:
{
"id": "uuid",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"email": "[email protected]",
"is_chirpy_red": false
}POST /api/loginRequest:
{
"email": "[email protected]",
"password": "super-secret-password"
}Response:
{
"id": "uuid",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"email": "[email protected]",
"is_chirpy_red": false,
"token": "<jwt>",
"refresh_token": "<refresh-token>"
}POST /api/refreshHeaders:
Authorization: Bearer <refresh-token>Response:
{
"token": "<new-jwt>"
}POST /api/revokeHeaders:
Authorization: Bearer <refresh-token>Response:
204 No ContentPUT /api/usersHeaders:
Authorization: Bearer <jwt>Request:
{
"email": "[email protected]",
"password": "new-password"
}Response:
{
"id": "uuid",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-02T00:00:00Z",
"email": "[email protected]",
"is_chirpy_red": false
}POST /api/chirpsHeaders:
Authorization: Bearer <jwt>Request:
{
"body": "Hello Chirpy!"
}Response:
{
"id": "uuid",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z",
"body": "Hello Chirpy!",
"user_id": "uuid"
}GET /api/chirpsOptional query parameters:
author_id=<user-id>
sort=asc
sort=desc
Examples:
GET /api/chirps
GET /api/chirps?sort=desc
GET /api/chirps?author_id=<user-id>
GET /api/chirps?author_id=<user-id>&sort=descGET /api/chirps/{chirpID}DELETE /api/chirps/{chirpID}Headers:
Authorization: Bearer <jwt>Responses:
204 No Content
403 Forbidden
404 Not FoundGET /api/healthzResponse:
OK
GET /admin/metricsReturns an HTML page showing application metrics.
POST /admin/resetDevelopment mode only.
POST /api/polka/webhooksHeaders:
Authorization: ApiKey <polka-api-key>Request:
{
"event": "user.upgraded",
"data": {
"user_id": "uuid"
}
}Response:
204 No ContentAuthorization: Bearer <jwt>Used for:
- Creating chirps
- Updating users
- Deleting chirps
Authorization: Bearer <refresh-token>Used for:
- Refreshing access tokens
- Revoking refresh tokens
Authorization: ApiKey <polka-key>Used for:
- Webhook authentication
The project uses GitHub Actions for automated releases.
Every merge into the release branch:
- Runs tests
- Builds the application
- Generates the next semantic version
- Creates a GitHub Release
- Publishes release binaries
- Chirp editing
- Pagination
- Rate limiting
- Docker support
- OpenAPI/Swagger documentation
- Structured logging
- Role-based authorization
This project was built as part of the Boot.dev Backend Development curriculum and expanded into a complete REST API backend in Go.