Your `friendship` table is a simple adjacency list representation for an undirected graph. As you point out, the main trouble in using a relational database for a true graph model is to process graph traversals, which you can do with nested inner queries, joins, or Postgres' WITH RECURSIVE. A graph database, for that reason, is usually optimized as a very efficient sequential JOIN machine, where you can imagine successive JOINS as connecting the endpoints of edges along a graph path.
By the way, if you're interested in graph representations for efficient querying, check out TripleStores [1], and the Wiki list on subject-predicate-object databases [2].
[1] https://en.wikipedia.org/wiki/Triplestore [2] https://en.wikipedia.org/wiki/List_of_subject-predicate-obje...