Other Notes¶
There are a few other things to be aware of while using the API:
Explore the Graph¶
GraphQL is still quite new, so we figured it might be good to provide some helpful tips on how to think about the data and how you'll use the API.
First, it is probably well worth your time to play around in GraphiQL to explore the API and data. It was heavily used when developing the API and writing tests, and is a very powerful tool, particularly when you make use of the self-documenting nature of the graph.
When you're thinking about how to query don't necessarily try to
replicate your old API calls exactly. For example, perhaps you were
grabbing all bills that met a given criteria and then grabbing all
sponsors contact details. This can now be done in one call by traversing
from the bills-root
root node into the BillSponsorshipNode
and then up to the
PersonNode
and finally to the ContactDetailNode
This may sound
complex at first, but once you get the hang of it, it really does unlock
a ton of power and will make your apps more powerful and efficient.
Pagination¶
In several places (such as the bills-root
and BillNode
's votes
) we mention that nodes are paginated.
What this means in practice is that instead of getting back the
underlying node type, say BillNode
, directly, you'll get back
BillConnectionNode
or similar. (In practice there are connection node
types for each paginated type, but all work the same way in our case.)
Arguments¶
Each paginated endpoint accepts any of four parameters:
first
- given an integer N, only return the first N nodeslast
- given an integer N, only return the last N nodesafter
- combined withfirst
, will return first N nodes after a given "cursor"before
- combined withlast
, will return last N nodes before a given "cursor"
So typically you'd paginate using first
, obtaining a cursor, and then
calling the API again with a combination of first
and after
.
The same process could be carried out with last
and before
to
paginate in reverse.
Responses¶
Let's take a look at everything that pagination makes available:
{
bills(first:20) {
edges {
node {
title
}
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
endCursor
startCursor
}
totalCount
}
}
You'll see that the connection node has three nodes: edges
,
pageInfo
, and totalCount
-
edges
- a list of objects that each have anode
andcursor
attribute:-
node
- the underlying node type, in our caseBillNode
cursor
- an opaque cursor for this particular item, it can be used with thebefore
andafter
parameters each paginated node accepts as arguments.
-
pageInfo
- a list of helpful pieces of information about this page:-
hasNextPage
- boolean that is true if there is another page after thishasPreviousPage
- boolean that is true if there is a page before thisendCursor
- last cursor in the set of edges, can be used withafter
to paginate forwardstartCursor
- first cursor in the set of edges, can be used withbefore
to paginate backwards
-
totalCount
- total number of objects available from this connection
In Practice¶
Let's say you want to get all of the people matching a given criteria:
You'd start with a query for all people matching your criteria, ensuring to set the page size to no greater than the maximum:
{
people(memberOf: "Some Organization", first: 100) {
edges {
node {
name
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
Let's say we got back a list of 100 edges and our pageInfo
object
looked like:
{
"hasNextPage": true,
"endCursor": "ZXJyYXlxb20uZWN0aW9uOjA="
}
So you'd make another call:
{
people(memberOf: "Some Organization", first: 100, after:"ZXJyYXlxb20uZWN0aW9uOjA=" ) {
edges {
node {
name
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
And let's say in this case you got back only 75 edges, and our
pageInfo
object looks like:
{
"hasNextPage": false,
"endCursor": "AXjYylxX2bu1wxa9uunnb="
}
We'd stop iteration at this point, of course, if hasNextPage had been true, we'd continue on until it wasn't.
Renaming fields¶
A really useful trick that is often overlooked is that you can rename fields when retrieving them, for example:
{
republicans: people(memberOf: "Republican", first: 5) {
edges {
node {
full_name: name
}
}
}
}
Would give back:
{
"data": {
"republicans": {
"edges": [
{
"node": {
"full_name": "Michelle Udall"
}
},
{
"node": {
"full_name": "Kimberly Yee"
}
},
{
"node": {
"full_name": "Regina E. Cobb"
}
},
{
"node": {
"full_name": "Michelle B. Ugenti-Rita"
}
},
{
"node": {
"full_name": "David Livingston"
}
}
]
}
}
}
Note that we're both renaming a top-level node here as well as a piece of data within the query.
You can also use this to query the same root node twice (doing so without renaming isn't allowed since it results in a name conflict).
For example:
{
republicans: people(memberOf: "Republican", first: 5) {
edges {
node {
full_name: name
}
}
}
democrats: people(memberOf: "Democratic", first: 5) {
edges {
node {
full_name: name
}
}
}
}
Fuzzy Date Format {#date-format}¶
Unless otherwise noted (most notably createdAt
and updatedAt
all
date objects are "fuzzy". Instead of being expressed as an exact date,
it is possible a given date takes any of the following formats:
- YYYY
- YYYY-MM
- YYYY-MM-DD
- YYYY-MM-DD HH:MM:SS (if times are allowed)
Action/Vote times are all assumed to be in the state capitol's time zone.
Times related to our updates such as updatedAt and createdAt are in UTC.
Name Matching¶
In several places such as bill sponsorships and votes you'll notice
that we have a raw string representing a person or organization as well
as a place for a link to the appropriate OrganizationNode
or PersonNode
.
Because of the way we collect the data from states, we always collect the raw data and later make an attempt to (via a mix of automated matching and manual fixes) connect the reference with data we've already collected.
In many cases these linkages will not be provided, but with some upcoming new tools to help us improve this matching we'll be able to dramatically improve the number of matched entities in the near future.