Inspired by Build a REST directive with GraphQL Tools by Jaime Barton which you can watch here:
Jaime shows you how to build a custom @rest directive with GraphQL Tools to resolve data from a JSON API -- but, we'll use Redwood Directives instead to show how you can still implement this feature but not have to go deep into the GraphQL structure.
And ... we'll add a way to fetch a single item from a JSON API ... and tests!
Redwood Directives are a powerful feature, supercharging your GraphQL-backed Services.
You can think of directives like "middleware" that let you run reusable code during GraphQL execution to perform tasks like authentication and formatting.
Redwood uses them to make it a snap to protect your API Services from unauthorized access.
Here we call those types of directives Validators.
You can also use them to transform the output of your query result to modify string values, format dates, shield sensitive data, and more! We call those types of directives Transformers.
We'll be using the Transformer Directive type to imoplemen the @rest
directive.
- yarn rw g directive rest --type=transformer
- rest-directive % yarn workspace api add cross-undici-fetch
We'll use the https://jsonplaceholder.typicode.com
JSON api demo to get Photos and Posts.
You can browse the JSON response for https://jsonplaceholder.typicode.com/posts
to see how we match the SDL type Post
to the example data:
[
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
},
{
"userId": 1,
"id": 2,
"title": "qui est esse",
"body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
},
{
"userId": 1,
"id": 3,
"title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
"body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
},
...
]
// api/src/graphql/posts.sdl.ts
export const schema = gql`
type Post {
id: Int!
title: String @uppercase
body: String
userId: String
}
type Query {
post(id: Int!): Post
@rest(url: "https://jsonplaceholder.typicode.com/posts/:id")
@skipAuth
posts: [Post]
@rest(url: "https://jsonplaceholder.typicode.com/posts")
@skipAuth
}
`
// api/src/graphql/photos.sdl.ts
export const schema = gql`
type Photo {
id: Int!
albumId: Int!
title: String
thumbnailUrl: String
userId: String
}
type Query {
photo(id: Int!): Photo
@rest(url: "https://jsonplaceholder.typicode.com/photo/:id")
@skipAuth
photos: [Photo]
@rest(url: "https://jsonplaceholder.typicode.com/photos")
@skipAuth
}
`
You can see the full @rest transformer directive implementation.
TLDR;
- It extracts the
url
set in the directive from itdirectiveArgs
- Extracts any query
args
to be used to replace the named parameters. - Replace those params if needed (this is for the fetch post by by query)
- Construct a url
- Fetch from this url
- Return the response
Yes, you can mock the JSON API response to test that your directive resturns the expected data.
See how the mockRedwoodDirective
testing utility let's you pass in the directiveArtgs and args for the url with a mocked response:
// api/src/directives/rest/rest.test.ts
jest.mock('cross-undici-fetch', () => ({
fetch: (url) => {
switch (url) {
case 'https://example.com':
return {
ok: true,
json: async () => {
return POSTS_JSON
},
}
case 'https://example.com/1':
return {
ok: true,
json: async () => {
return POSTS_JSON[0]
},
}
}
},
}))
// ...
describe('demonstrate use of args for named parameter replacement', () => {
it('with a url for a single item, returns the json response for that json api url', async () => {
const mockExecution = mockRedwoodDirective(rest, {
mockedResolvedValue: '',
directiveArgs: { url: 'https://example.com/:id' },
args: { id: 1 },
})
await expect(mockExecution()).resolves.toEqual(POSTS_JSON[0])
})
})
See @rest Directive Unit Test Code
Now with these SDL and Directives you can query the RedwoodJS GraphQL API and use RedwoodJS cells to render Posts and photos -- live examples!
See the web side code for this.
- Add
headers
so can pass api tokens or other Authorization headers - Support
POST
andGET
methods - Chain with another Transformer directive to reshape the response (transform JSON API data to match a different SDL) (maybe?)
You may be wondering, why use this @rest
directive instead of implementing the same SDL and the implementing a service called posts
and post
and then in that service, use the same cross-unidici-fetch to fetch the url?
And the answer, is ... I'd probably implement this type of feature using a service.
With a service, then a service can call another service (if some other feature needs a few posts to process)... or I could have a serverless function call the posts service to fetch for a RESTful API :).
I might even create a fetch client specifically for the JSON API in api/lib
.
But, for very simple REST API fetches one can save quite a bit of code by not having to implement a service method for each endpoint and each get many and get singular.
This Office Hours Example is of a showcase of the power and simplicity of creating Transformer Directives and how one can test them as well.
Welcome to RedwoodJS!
Prerequisites
- Redwood requires Node.js (>=14.19.x <=16.x) and Yarn (>=1.15)
- Are you on Windows? For best results, follow our Windows development setup guide
Start by installing dependencies:
yarn install
Then change into that directory and start the development server:
cd my-redwood-project
yarn redwood dev
Your browser should automatically open to http://localhost:8910 where you'll see the Welcome Page, which links out to a ton of great resources.
The Redwood CLI
Congratulations on running your first Redwood CLI command! From dev to deploy, the CLI is with you the whole way. And there's quite a few commands at your disposal:
yarn redwood --help
For all the details, see the CLI reference.
The best way to learn Redwood is by going through the comprehensive tutorial and joining the community (via the Discourse forum or the Discord server).
- Stay updated: read Forum announcements, follow us on Twitter, and subscribe to the newsletter
- Learn how to contribute