Four options, honestly compared. Every column loses at least one row, because a comparison that one option wins outright is not a comparison.
The matrix
| Token API | Subgraph | Substreams | Firehose | |
|---|---|---|---|---|
| Nothing to author | yes | no | no | no |
| Custom data model | no | yes | yes | yes |
| GraphQL API out of the box | no | yes | no | no |
| Handles very large backfills well | yes | no | yes | yes |
| Output to your own database or warehouse | no | no | yes | yes |
| Multiple chains in one integration | yes | no | partly | no |
| No curation signal needed to be served | yes | no | n/a | n/a |
| Maintainable by a team without Rust | yes | yes | no | no |
| You operate no infrastructure | yes | yes | partly | no |
The bolded cells are where each option loses. Note that the subgraph column, the default answer for most people, loses four rows. That is not an argument against it. It is the reason the other services exist.
The decision, in order.
1. Is your question about tokens? Balances, transfers, prices, NFT ownership. If yes, use the Token API and stop. This is the most commonly skipped step and it costs people weeks.
2. Do you need a typed API over one protocol’s state? If yes, write a subgraph. This is the default and it is right most of the time.
3. Is your destination not GraphQL? A warehouse, a stream, your own database. If yes, Substreams with a sink.
4. Is backfill time the binding constraint? History is large, a handler loop takes days, and you cannot wait. Substreams, possibly feeding a subgraph so you keep the GraphQL API.
5. Are you integrating a chain or operating indexing infrastructure? Firehose.
The combination people miss.
Substreams feeding a subgraph. You keep GraphQL for your consumers and change what fills it. When a subgraph is right but its backfill is unworkable, this is usually the answer rather than abandoning the API shape.
Token API plus a subgraph. Almost every application needs both token data and its own protocol state. Using each for its half and joining in your application is straightforward and common.
Costs that are easy to miss.
- A subgraph needs signal. Publishing does not get it served. That is a real cost and a real delay, covered in the curator path.
- Substreams needs Rust. Not a snobbery point, a staffing one. A module one person understands is a liability the day that person is on holiday.
- Firehose is infrastructure. If you are not already operating chain infrastructure, this is a large commitment for a data problem.
- The Token API’s model is fixed. If your question drifts outside it, you migrate.
Your subgraph is the right shape but backfilling five years of history takes days. Best move?
The API shape is right and the fill mechanism is wrong, so change the fill mechanism. Abandoning GraphQL because backfill is slow solves the wrong problem.
Which row does the subgraph column lose?
A subgraph's output is entities served over GraphQL. If your destination is a warehouse or a stream, that is a Substreams or Firehose job. The subgraph column loses four rows in total, which is why the other services exist.
What is the most commonly skipped step in this decision?
Developers who have learned about subgraphs reach for one by reflex. If the question is about balances, transfers, prices or NFT ownership, the answer already exists and authoring one costs weeks for nothing.
0 of 3 answered