Add full monorepo: virtual-banker, backend, frontend, docs, scripts, deployment
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
351
docs/specs/api/graphql-api.md
Normal file
351
docs/specs/api/graphql-api.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# GraphQL API Specification
|
||||
|
||||
## Overview
|
||||
|
||||
This document specifies the GraphQL API for the explorer platform, providing flexible queries, mutations for user data, and subscriptions for real-time updates.
|
||||
|
||||
**Endpoint**: `https://api.explorer.d-bis.org/graphql`
|
||||
|
||||
## Schema Overview
|
||||
|
||||
### Core Types
|
||||
|
||||
#### Block
|
||||
|
||||
```graphql
|
||||
type Block {
|
||||
chainId: Int!
|
||||
number: BigInt!
|
||||
hash: String!
|
||||
parentHash: String!
|
||||
timestamp: DateTime!
|
||||
miner: Address
|
||||
transactionCount: Int!
|
||||
gasUsed: BigInt!
|
||||
gasLimit: BigInt!
|
||||
transactions: [Transaction!]!
|
||||
}
|
||||
```
|
||||
|
||||
#### Transaction
|
||||
|
||||
```graphql
|
||||
type Transaction {
|
||||
chainId: Int!
|
||||
hash: String!
|
||||
block: Block
|
||||
blockNumber: BigInt
|
||||
from: Address!
|
||||
to: Address
|
||||
value: BigInt!
|
||||
gasPrice: BigInt
|
||||
gasUsed: BigInt
|
||||
status: TransactionStatus!
|
||||
logs: [Log!]!
|
||||
traces: [Trace!]!
|
||||
}
|
||||
```
|
||||
|
||||
#### Address
|
||||
|
||||
```graphql
|
||||
type Address {
|
||||
address: String!
|
||||
chainId: Int!
|
||||
balance: BigInt!
|
||||
balanceUSD: Float
|
||||
transactionCount: Int!
|
||||
transactions: TransactionConnection!
|
||||
tokens: [TokenBalance!]!
|
||||
nfts: [NFT!]!
|
||||
label: String
|
||||
tags: [String!]!
|
||||
isContract: Boolean!
|
||||
contract: Contract
|
||||
}
|
||||
```
|
||||
|
||||
#### Token
|
||||
|
||||
```graphql
|
||||
type Token {
|
||||
address: String!
|
||||
chainId: Int!
|
||||
name: String
|
||||
symbol: String
|
||||
type: TokenType!
|
||||
decimals: Int
|
||||
totalSupply: BigInt
|
||||
holderCount: Int!
|
||||
transfers: TokenTransferConnection!
|
||||
holders: HolderConnection!
|
||||
}
|
||||
```
|
||||
|
||||
#### Contract
|
||||
|
||||
```graphql
|
||||
type Contract {
|
||||
address: String!
|
||||
chainId: Int!
|
||||
name: String
|
||||
verificationStatus: VerificationStatus!
|
||||
sourceCode: String
|
||||
abi: JSON
|
||||
compilerVersion: String
|
||||
createdAt: DateTime
|
||||
}
|
||||
```
|
||||
|
||||
## Queries
|
||||
|
||||
### Block Queries
|
||||
|
||||
```graphql
|
||||
query GetBlock($chainId: Int!, $number: BigInt!) {
|
||||
block(chainId: $chainId, number: $number) {
|
||||
number
|
||||
hash
|
||||
timestamp
|
||||
transactionCount
|
||||
transactions {
|
||||
hash
|
||||
from
|
||||
to
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Transaction Queries
|
||||
|
||||
```graphql
|
||||
query GetTransaction($chainId: Int!, $hash: String!) {
|
||||
transaction(chainId: $chainId, hash: $hash) {
|
||||
hash
|
||||
from
|
||||
to
|
||||
value
|
||||
status
|
||||
logs {
|
||||
address
|
||||
topics
|
||||
data
|
||||
}
|
||||
traces {
|
||||
type
|
||||
from
|
||||
to
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Address Queries
|
||||
|
||||
```graphql
|
||||
query GetAddress($chainId: Int!, $address: String!) {
|
||||
address(chainId: $chainId, address: $address) {
|
||||
address
|
||||
balance
|
||||
balanceUSD
|
||||
transactionCount
|
||||
transactions(first: 10) {
|
||||
edges {
|
||||
node {
|
||||
hash
|
||||
blockNumber
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
tokens {
|
||||
token {
|
||||
symbol
|
||||
name
|
||||
}
|
||||
balance
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Search Query
|
||||
|
||||
```graphql
|
||||
query Search($query: String!, $chainId: Int) {
|
||||
search(query: $query, chainId: $chainId) {
|
||||
... on Block {
|
||||
number
|
||||
hash
|
||||
}
|
||||
... on Transaction {
|
||||
hash
|
||||
from
|
||||
to
|
||||
}
|
||||
... on Address {
|
||||
address
|
||||
label
|
||||
}
|
||||
... on Token {
|
||||
address
|
||||
symbol
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mutations
|
||||
|
||||
### User Preferences
|
||||
|
||||
```graphql
|
||||
mutation UpdateWatchlist($chainId: Int!, $address: String!, $label: String) {
|
||||
addToWatchlist(chainId: $chainId, address: $address, label: $label) {
|
||||
success
|
||||
watchlistItem {
|
||||
address
|
||||
label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutation RemoveFromWatchlist($chainId: Int!, $address: String!) {
|
||||
removeFromWatchlist(chainId: $chainId, address: $address) {
|
||||
success
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Address Labels
|
||||
|
||||
```graphql
|
||||
mutation AddAddressLabel($chainId: Int!, $address: String!, $label: String!) {
|
||||
addAddressLabel(chainId: $chainId, address: $address, label: $label) {
|
||||
success
|
||||
label {
|
||||
address
|
||||
label
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Subscriptions
|
||||
|
||||
### New Blocks
|
||||
|
||||
```graphql
|
||||
subscription NewBlocks($chainId: Int!) {
|
||||
newBlock(chainId: $chainId) {
|
||||
number
|
||||
hash
|
||||
timestamp
|
||||
transactionCount
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Address Transactions
|
||||
|
||||
```graphql
|
||||
subscription AddressTransactions($chainId: Int!, $address: String!) {
|
||||
addressTransaction(chainId: $chainId, address: $address) {
|
||||
hash
|
||||
from
|
||||
to
|
||||
value
|
||||
status
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pending Transactions
|
||||
|
||||
```graphql
|
||||
subscription PendingTransactions($chainId: Int!) {
|
||||
pendingTransaction(chainId: $chainId) {
|
||||
hash
|
||||
from
|
||||
to
|
||||
value
|
||||
gasPrice
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Resolver Architecture
|
||||
|
||||
### DataLoader Pattern
|
||||
|
||||
**Purpose**: Prevent N+1 query problems.
|
||||
|
||||
**Implementation**:
|
||||
- Batch load related entities
|
||||
- Cache within request context
|
||||
- Example: Batch load all blocks for transactions in a query
|
||||
|
||||
### Field Resolvers
|
||||
|
||||
**Strategy**: Lazy loading of related fields.
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
Block: {
|
||||
transactions: (block, args, context) => {
|
||||
return context.loaders.transactions.load({
|
||||
chainId: block.chainId,
|
||||
blockNumber: block.number
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Query Complexity
|
||||
|
||||
**Limits**:
|
||||
- Max depth: 10 levels
|
||||
- Max complexity score: 1000
|
||||
- Rate limit based on complexity
|
||||
|
||||
### Caching
|
||||
|
||||
**Strategy**:
|
||||
- Cache frequently accessed fields (blocks, tokens)
|
||||
- Cache duration: 1-60 seconds depending on data freshness needs
|
||||
- Invalidate on updates
|
||||
|
||||
## Authentication
|
||||
|
||||
Same as REST API: API Key via header or OAuth Bearer token.
|
||||
|
||||
## Error Handling
|
||||
|
||||
**GraphQL Error Format**:
|
||||
```json
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"message": "Resource not found",
|
||||
"extensions": {
|
||||
"code": "NOT_FOUND",
|
||||
"requestId": "uuid"
|
||||
},
|
||||
"path": ["block"]
|
||||
}
|
||||
],
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- REST API: See `rest-api.md`
|
||||
- API Gateway: See `api-gateway.md`
|
||||
|
||||
Reference in New Issue
Block a user