Golang codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API.
For more information on how to this works with other frontends/backends, head over to the RealWorld repo.
# Auth
POST /users
POST /users/login
GET /user
PUT /user
# Article, Favorite, Comments
POST /articles
GET /articles
?tag={tag1,tag2}
&author={username}
&favorited={username}
&limit={limit}
&offset={offset}
GET /articles/feed
GET /articles/{slug}
PUT /articles/{slug}
DELETE /articles/{slug}
POST /articles/{slug}/favorite
DELETE /articles/{slug}/favorite
POST /articles/{slug}/comments
GET /articles/{slug}/comments
DELETE /articles/{slug}/comments/{commentId}
# Profiles
GET /profiles/{username}
POST /profiles/{username}/follow
DELETE /profiles/{username}/follow
# Tags
GET /tags- Users
- id
idx - email - unique
idx - username - unique
- password
- image
- bio
- created_at
- updated_at
- Articles
- id
- author_id
idx - slug - unique
- body
- title
- description
- created_at
- updated_at
- deleted_at
- Comments
- id
- article_id
- author_id
- body
- created_at
- deleted_at
- ArticleTags
- article_id
- tag_id
- Tags
- id
idx - name - unique
- Favorites
- user_id
- article_id
- Follows
- follower_id
- following_id- User vs. Article: One-to-Many
- Article vs. Comment: One-to-Many
- Article vs. Tag: Many-to-Many
- Follow User vs. User: Many-to-Many
- Favorite User vs. Article: Many-to-Many
- A project file structure that I like :)
- Concurrency with
errgroup - Transaction, Rollback (with
defer), Commit - Batch Insert to improve performance
values := []any{}must be[]anyto be used asvalues...indb.QueryContextQueryRowContextreturns a single rowQueryContextreturns multiple rows, which we have to.Close()manuallyLEFT JOINRETURNING idN+1 Problemlike get all author profiles after we get all the articlesWHERE id::TEXT = '...'to prevent exception whenidis not a uuidSELECT EXISTS (SELECT 1 FROM ... WHERE ...);to check for existenceCROSS JOINto combine all rows from one table to all rows in another tableSTRING_AGG(t.name, ', ')to aggregate tags into one column as a stringCOALESCE(ARRAY_AGG(DISTINCT t.name) FILTER (WHERE t.name IS NOT NULL), '{}')to aggregate tags into one column as an arrayvar tags pq.StringArrayrepresents a one-dimensional array of the PostgreSQL character types, then[]string(tags)to get array of stringsCOUNT(DISTINCT id)to count the number of distinct rowsCOUNT(*) OVER()to count all rows that match theWHEREbefore applyingLIMITandOFFSETWHERE ('' = $1 OR username = $1)skip if emptyON CONFLICT (name) DO UPDATE SET name=EXCLUDED.nameif insert (or update) conflict update the old value to new value (same)ON CONFLICT DO NOTHING
- Tag can be a slice of strings
- Allow update article's tags
- Add tests with standard
testingpackage - Add cache with Redis
- Add notifications with SSE + RabbitMQ + Redis Pub/Sub Architecture
Run build make command with tests
make allBuild the application
make buildRun the application
make runCreate DB container
make docker-runShutdown DB Container
make docker-downDB Integrations Test
make itestLive reload the application
make watchRun the test suite
make testClean up binary from the last build
make cleanRun the test script
./api-test/run-api-tests.shWill produce an output like this:
┌─────────────────────────┬──────────────────┬─────────────────┐
│ │ executed │ failed │
├─────────────────────────┼──────────────────┼─────────────────┤
│ iterations │ 1 │ 0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│ requests │ 32 │ 0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│ test-scripts │ 48 │ 0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│ prerequest-scripts │ 18 │ 0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│ assertions │ 335 │ 0 │
├─────────────────────────┴──────────────────┴─────────────────┤
│ total run duration: 16.7s │
├──────────────────────────────────────────────────────────────┤
│ total data received: 32.01kB (approx) │
├──────────────────────────────────────────────────────────────┤
│ average response time: 9ms [min: 1ms, max: 61ms, s.d.: 17ms] │
└──────────────────────────────────────────────────────────────┘Contributions are welcome and highly appreciated!
This project follows the RealWorld API
spec — please make sure your changes
remain compliant.
