The Prop House backend stores all the signed data related to Auctions, Proposals, and Votes.
In addition to the normal REST API, the Prop House backend provides a GraphQL interface for querying data about Auctions, Proposals, and Votes. GraphQL should be the preferred method of fetching data because it:
- Allows you to request only the information you want
- Fetch related information in a single query
- Has a playground where you can interactively develop queries
The Prop House GraphQL interface features a playground that provides documentation on the query and response schemas and linting. You can open the playground at https://prod.backend.prop.house/graphql and build your own queries or copy the examples below.
The PH GraphQL interface provides a handful of queries:
Query Name | Arguments | Description |
---|---|---|
auction |
id: Int |
Fetch a single auction record based on its ID |
auctions |
offset: Int limit: Int where: Auction |
Fetch the Auctions that match the criteria Auction provided as where |
auctionsByStatus |
offset: Int limit: Int status: AuctionStatus |
Fetch Auctions that match the provided status |
proposal |
id: Int |
Fetch a proposal based on its ID |
community |
id: Int |
Fetch a community based on its ID |
communities |
None | Fetch all communities available on the Prop House |
findByAddress |
address: string |
Fetch a Community based on its token address |
A common use case is to fetch all the auctions for a given Community. This query could be used to render a page that shows the Auctions for a Community and some basic information about their proposals.
{
findByAddress(address: "0x898a7dbfddf13962df089fbc8f069fa7ce92cdbb") {
name
description
auctions {
description
numWinners
fundingAmount
status
proposals {
title
address
voteCount
}
}
}
}
The above example requests some basic information about the Nouns Japan Community, the Auctions within that community, as well as the Proposals in each Auction.
You'll notice that we're not loading the entirety of each entity, instead we're only loading a couple properties that'd be required for rendering a simple set of cards. With GraphQL you can request only what you need, and receive only what you ask for.
We can also jump half way into this relationship chain and fetch only the Auctions that are currently Open and accepting proposals. This may be useful for a landing page that shows users where they can submit proposals to.
{
auctionsByStatus(status: Open) {
title
numWinners
fundingAmount
proposalEndTime
description
community {
name
}
proposals {
id
title
tldr
}
}
}
Similar to the first example, if we know that an Auction is Open the only date we really worry about is the Proposal deadline date. Fetching the other dates would be excessive if we weren't rendering them.
Within each query we can choose to load its related entities or not, we could even load several layers of relations, although it should be avoided...
{
proposal(id: 125) {
id
title
auction {
id
proposals {
id
title
voteCount
}
}
}
}
This could be the case where we are attempting to render a single proposal but want to fetch some information about the other proposals int he same Auction. With a REST API this would require a handful of requests or the API would return too much data. With GraphQL we can ask for just what we need.
The Auctions query allows for providing a partial Auction
to match against. Note that the where will match all properties with a logical AND
.
query AllUmaAuctions {
auctions(where: { currencyType: "UMA" }) {
title
id
startTime
votingEndTime
proposalEndTime
community {
contractAddress
}
proposals {
title
voteCount
tldr
}
}
}
Now, go ahead and play with the GraphQL Playground at https://prod.backend.prop.house/graphql and try some of these example queries. See the Schema
and Docs
tabs on the right side for more information about the shapes and fields available.
$ yarn
# Start containerized development services
$ docker-compose up -d
# Perform database migrations
$ yarn migration:run
# development
$ yarn start
# watch mode
$ yarn start:dev
Since Prop House is now in production, TypeORM will no longer automatically synchronize changes. In order to make changes to entities' database tables and relations you must generate a migration with your set of changes. This requires a running local database but TypeORM will generate migrations for you.
For example, you could want to add a property called language
to the Proposal
entity. You would add the property/column as normal to proposal.entity.ts
like so:
@Column()
language: string;
Previously TypeORM would automatically apply this change to the database, however automatic synchronization is highly discouraged for production applications. Instead developers should create migrations that have an up
and down
action to apply or roll back database schema changes. This keeps changes intentional and behavior explicit.
If you have the development database running (with or without data) you can have TypeORM generate the migrations for you automatically. The CLI will compare the schemas as defined by the project's entities with the schema of the connected database. If there are any differences, it will generate a migration to bring the two in sync. These migrations can then be committed along with your code changes for review by the other contributors.
To generate a migration for your schema change run:
$ yarn migration:generate AddProposalLanguage
This will cause the TypeORM CLI to generate a new migration file under src/db/migrations
with the SQL commands to apply and revert the schema changes. Note: this will compare against your locally connected database, so ensure you've run all migrations already.
When checking out new code or deploying to production, the following command will apply any local migrations:
$ yarn migration:run
The initial migration has been seeded to match the production database at type of writing. Syncing the production database up with the new migration scheme will require adding the following to the production migrations table manually:
ID | timestamp | name |
---|---|---|
auto | 1650506499501 | InitialMigration1650506499501 |
auto | 1651112951360 | AddTldr1651112951360 |
CREATE SEQUENCE migrations_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1;
CREATE TABLE "public"."migrations" (
"id" integer DEFAULT nextval('migrations_id_seq') NOT NULL,
"timestamp" bigint NOT NULL,
"name" character varying NOT NULL,
CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "migrations" ("id", "timestamp", "name") VALUES
(1, 1650506499501, 'InitialMigration1650506499501'),
(2, 1651112951360, 'AddTldr1651112951360');