-
Notifications
You must be signed in to change notification settings - Fork 54
GraphQL Flow
Our application uses GraphQL to make queries and mutations across the application. It controls the flow of data between the frontend, backend, and database for almost all logged-in users (i.e., it doesn't handle anonymous requests; we have separate Controller
classes for those.)
GraphQL has extensive documentation which you may find helpful: https://graphql.org/learn/
This page aims to document SimpleReport's specific GraphQL implementation for engineers new to the project.
GraphQL has both queries and mutations. Queries just return data from the database, while mutations let you edit the data in our database. A query example would be fetching the test queue for a given facility, while a mutation would be something like adding a new patient.
Our frontend calls queries and mutations which are sent to various Resolver
classes on the backend. These resolvers connect with our database, either directly or indirectly, to fetch or manipulate data as needed. The mutation/query definitions are shared in a contract between the frontend and backend, defined in .graphqls
files. The frontend must call these operations exactly as specified in the GraphQL file, and the backend must define a method to implement them.
We define our GraphQL mutations and types in main.graphqls
, admin.graphqls
, and wiring.graphqls
.
main.graphqls
defines most of the interactions seen by end users - adding patients, facilities, tests, api users, etc, as well as fetching data like tests, organization name, and the patient list. It also defines several types
that are used across different queries and mutations, such as Organizations and Facilities. These types are usually closely related to our database tables, but that's an artificial construct - there's nothing that directly ties the two.
admin.graphqls
defines superadmin or support-capable queries and mutations. These include mutations like resending a group of results to ReportStream, creating or identity verifying a new organization, and marking api users or facilities as deleted or undeleted.
wiring.graphqls
defines some common types for GraphQL usage, including permission levels, roles, and some statuses. It does not currently define any mutations or queries.
Whenever these .graphqls
files are changed, you need to run yarn codegen
on the frontend. There's an additional generated file, graphql.tsx
that converts all our queries, mutations, and types to Typescript-compatible types. Before the team used this generated file, to use a mutation or query on the frontend you'd need to first re-define the mutation/query as a Typescript type within your frontend file. It was very annoying, so just use the generated types instead!
Let's follow the path of a GraphQL query all the way through our application. (Note that this was written 12/1/2021, apologies if it's out of date by the time you're reading this.)
In this example, we'll look at fetching the list of users on the Manage Users
page.
ManageUsersContainer.tsx
and ManageUsers.tsx
hold most of the frontend logic for this page. The query we're interested in here is getUsersAndStatus
, which fetches basic user and account status information for the user list. It returns the user's id, name, email address, and account status (as string) for each user in an organization.
The query itself is defined in main.graphqls
, but for our purposes on the frontend, it's contained in the generated Typescript file graphql.tsx
.
Notice that the query called in ManageUsersContainer
has a refetch
parameter, which gets passed to ManageUsers
. This refetch allows us to call the query again in the event that the data underlying the query changes, i.e. a user has been added to or removed from the organization.
The initial state within ManageUsers
also contains a list of users
, which is the output of the initial call to getUsersAndStatus
. This list is sorted and passed into the UsersSideNav
component.
The getUsersWithStatus
query is defined in UserResolver.java
. This in turn calls the ApiUserService
method getUsersAndStatusInCurrentOrg
.
Unlike most other GraphQL methods, this query does not only look at the database for data. It also queries Okta to retrieve the user's status. The Okta calls are directed to LiveOktaRepository
, which fetches the list of all users for the given organization and returns a map of their email to account status. Then, the database is directly queried with the ApiUserRepository
, returning all database objects for a given set of login emails.
The results of these two queries are merged into an ApiUserWithStatus
object, which contains all the information that's expected to return from the original GraphQL query.
Coming soon!
- Getting Started
- [Setup] Docker and docker compose development
- [Setup] IntelliJ run configurations
- [Setup] Running DB outside of Docker (optional)
- [Setup] Running nginx locally (optional)
- [Setup] Running outside of docker
- Accessing and testing weird parts of the app on local dev
- Accessing patient experience in local dev
- API Testing with Insomnia
- Cypress
- How to run e2e locally for development
- E2E tests
- Database maintenance
- MailHog
- Running tests
- SendGrid
- Setting up okta
- Sonar
- Storybook and Chromatic
- Twilio
- User roles
- Wiremock
- CSV Uploader
- Log local DB queries
- Code review and PR conventions
- SimpleReport Style Guide
- How to Review and Test Pull Requests for Dependabot
- How to Review and Test Pull Requests with Terraform Changes
- SimpleReport Deployment Process
- Adding a Developer
- Removing a developer
- Non-deterministic test tracker
- Alert Response - When You Know What is Wrong
- What to Do When You Have No Idea What is Wrong
- Main Branch Status
- Maintenance Mode
- Swapping Slots
- Monitoring
- Container Debugging
- Debugging the ReportStream Uploader
- Renew Azure Service Principal Credentials
- Releasing Changelog Locks
- Muting Alerts
- Architectural Decision Records
- Backend Stack Overview
- Frontend Overview
- Cloud Architecture
- Cloud Environments
- Database ERD
- External IDs
- GraphQL Flow
- Hibernate Lazy fetching and nested models
- Identity Verification (Experian)
- Spring Profile Management
- SR Result bulk uploader device validation logic
- Test Metadata and how we store it
- TestOrder vs TestEvent
- ReportStream Integration
- Feature Flag Setup
- FHIR Resources
- FHIR Conversions
- Okta E2E Integration
- Deploy Application Action
- Slack notifications for support escalations
- Creating a New Environment Within a Resource Group
- How to Add and Use Environment Variables in Azure
- Web Application Firewall (WAF) Troubleshooting and Maintenance
- How to Review and Test Pull Requests with Terraform Changes