-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: increase documentation across the codebase #503
base: master
Are you sure you want to change the base?
Changes from 10 commits
023b4cd
cf60a3d
1c37c4c
ab55a40
8d0ffae
b238d5a
67b618a
7f26386
35eee89
9aea528
1f7bd7d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
# graphql-node-resource | ||
|
||
This library implements the [Node GraphQL type defined by Relay](https://relay.dev/docs/guides/graphql-server-specification/#object-identification). Relay requires that a GraphQL server | ||
has a way to identify and refetch GraphQL resources. Its meant to be used within a GraphQL API that fetches its types from a REST API. | ||
|
||
|
||
```mermaid | ||
graph LR; | ||
frontend(frontend using relay)-->graphql-api(GraphQL API); | ||
graphql-api-->rest-api(REST API); | ||
rest-api-->graphql-api; | ||
graphql-api-->frontend; | ||
``` | ||
_example architecture_ | ||
|
||
|
||
## What is a Node? | ||
|
||
A `Node` is a refetchable GraphQL resource. All `Node`s must have a _globally unique ID_ and be fetchable via this ID. Relay will refetch these resources in the following way: | ||
|
||
``` | ||
node(id: $resource_id) { | ||
... on Foo { | ||
bar | ||
baz | ||
} | ||
} | ||
``` | ||
|
||
## The Node Class | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: this is kind of ambiguous to me, should I be reading this something like, "The NodeType, a sensible Node Implementation"? |
||
|
||
The [NodeType](./src/types/NodeType.ts#) class does two things: | ||
1. it defines the `Node` type required by Relay | ||
1. it implements a solution to asynchronously resolve the Node by fetching it from a backing REST API | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: just want to make sure I understanding the semantics clearly, this is most likely just me making noise...
|
||
|
||
It is an extension of the [GraphQLObjectType](https://graphql.org/graphql-js/type/#graphqlobjecttype) provided by `graphql-js`. | ||
|
||
Every node consists of a backing HTTP REST resource that will be used to fetch the node. This library exposes an [HttpResource class](./src/resources/HttpResource.ts) that implements basic CRUD API operations. | ||
|
||
## Usage | ||
|
||
### Creating a new Node type | ||
|
||
If your API is a standard CRUD API, then all you need to do is create a new instance of a NodeType: | ||
|
||
``` | ||
import {GraphQLInt} from 'graphql'; | ||
|
||
// Foo.ts | ||
export default new NodeType({ | ||
// this determines the GraphQL type name | ||
name: 'Foo', | ||
|
||
// this provides graphql documentation on this type | ||
description: 'Foo is the catch all example for all things programming', | ||
|
||
// returns an object containing all of the fields for this graphql type | ||
fields() { | ||
return { | ||
bar: { | ||
type: GraphQLInt, | ||
description: 'A unit of Foo' | ||
} | ||
} | ||
}, | ||
|
||
createResource(ctx) { | ||
// as long as the `/foo` endpoint follows standard CRUD, then HttpResource should be able | ||
// to handle fetching it correctly | ||
return new HttpResource({ | ||
endpoint: "/foo" | ||
}) | ||
} | ||
}); | ||
|
||
|
||
``` | ||
|
||
|
||
|
||
### Adding the Node Field to Your Query Object | ||
|
||
In order to fulfill the contract required by Relay, you need to expose a field `node` on your `Query` type. | ||
|
||
``` | ||
// query.ts | ||
import { getConfig } from '@4c/graphql-node-resource/config'; | ||
|
||
const config = getConfig(); | ||
|
||
export default new GraphQLObjectType({ | ||
name: 'Query', | ||
|
||
fields: { | ||
node: config.nodeField, | ||
nodes: config.nodesField, | ||
}, | ||
}) | ||
``` | ||
|
||
This will expose `node` and `nodes` on your Query graphql type. | ||
|
||
### Resolving a Node Type | ||
|
||
There are two ways to resolve a Node type from a parent type: | ||
1. use the `NodeType.getResource(context)` function to get the HTTPResource object and use that to get the node | ||
2. resolve the field to an object containing an id such as `{id: 'foo123'} | ||
|
||
### Using Get Resource | ||
|
||
You can resolve the resource directly by getting the HTTPResource object from the NodeType instance: | ||
``` | ||
// Baz.ts | ||
export default new GraphQLObjectType({ | ||
name: 'Baz', | ||
fields: { | ||
foo: { | ||
type: new GraphQLList(Foo), | ||
resolve(context) { | ||
const resource = Foo.getResource(context) as HTTPResource; | ||
return resource.get(); // get a list of Foos | ||
} | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
This is useful when you don't have an ID and need to resolve a list of resources or | ||
fetch a list of resources and then find the object you are looking for. | ||
|
||
### Passing an object with an id | ||
|
||
The `NodeType` class provides default resolvers for all defined fields. These resolvers will try to fetch the Node from the provided resource and then resolve to the matching field in the response. In order to do this, the `id` field must be present in the object that is being resolved. | ||
|
||
In the example below, the resolver for the `foo` field returns an object with an `id` property. The default resolvers provided by the NodeType will use the `id` property to try and fetch the `Foo` resource. | ||
|
||
``` | ||
// Baz.ts | ||
export default new GraphQLObjectType({ | ||
name: 'Baz', | ||
fields: { | ||
foo: { | ||
type: Foo, | ||
resolve: (parent, args, context, info) => { | ||
return { | ||
id: 'foo:123' | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
This is useful when you have a list of IDs in the parent type. For instance, if `Baz` has a list of Foo `id`s, then you could pass the list of IDs to the Foo type. See [this example](example/types/Author.ts#L30) for more information. | ||
|
||
### Connections | ||
|
||
TODO | ||
|
||
### Fetching Nodes from a list endpoint | ||
|
||
TODO | ||
|
||
### Fetching non node types | ||
|
||
TODO | ||
|
||
### Performance | ||
|
||
TODO | ||
|
||
## Best Practices | ||
|
||
The following is a list of suggested best practices: | ||
1. One HttpResource for one GraphQL type: an HttpResource should be responsible for fetching a single Resource. | ||
|
||
## Example Application | ||
|
||
For a full end-to-end example of this library, see [example/README.md](example/README.md). This demonstrates how to connect | ||
this library to a real backend REST API. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Example Application | ||
|
||
This example application shows how to create a GraphQL API over a REST API utilizing the `graphql-node-resource` library. There | ||
are two parts to this example application: | ||
1. a REST API implemented via NodeJS with Express. The API exposes a simple blog resource and supports pagination. | ||
2. a GraphQL API that exposes the REST API. This shows how to utilize the `graphql-node-resource` library properly. | ||
|
||
## api/api.ts | ||
|
||
This is where the REST API lives. It may be useful to look at the entry and exit points of the API to see what the GraphQL server passes | ||
as input and expects as output. This is especially useful for looking at the paginated connection endpoints. Connections need to be supported | ||
via the underlying REST API. | ||
|
||
## GraphQL API | ||
|
||
The GraphQL API is composed of 3 main parts: | ||
1. `setup.ts` - this is required to tell the library how to fetch resources from the REST API as well as some basic configuration | ||
2. `types/*.ts` - these are the GraphQL types that make up the GraphQL API | ||
3. `index.ts` - this is the entry point for the GraphQL API. It calls the setup.ts module and combines all of the types | ||
|
||
## Starting Point | ||
|
||
A good starting point would be the `types/Query.ts` file. This is the entrypoint into any of the API endpoints. | ||
|
||
## Running the App | ||
|
||
```sh | ||
yarn # install the deps | ||
yarn start # start the app | ||
``` | ||
|
||
The app will run the GraphIQL tool. Navigate to the [outputted server url](http://localhost:8081/graphql) to interact with the API. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: maybe reference the gql interface, perhaps even update the code snippet below such that
Foo implements Node
? TheNode
interface is the fundamental part, right? it's the smart impl that's the magic?