23
migrate your redwood graphQL API with graphQL-helix and envelop
Note: As of Redwood v0.37, GraphQL Helix and Envelop are included by default in the Redwood API instead of Apollo Server.
If you are on v0.37 or later than you will not need the instructions in this article or the instructions in the official migration guide, Using GraphQL Envelop+Helix in Redwood v0.35+.
These instructions are only needed when upgrading from an older version of Redwood. This article is now mostly relevant as a historical snapshot of the development of the framework.
Back in January the Redwood team decided to modify the internals of Redwood to allow users to specify their own client instead of using Apollo Client. Within weeks @marceloalves created a new package for a React Query Provider and @tobbe showed how you could Switch to another GraphQL Client with graphql-hooks.
Over the last two months Dotan Simha from The Guild along with assistance from certified Redwood Whisperer @dthyresson have been working on similar modifications which will allow users to migrate away from Apollo Server to a different GraphQL server.
Hi, people of the Redwood! :)
I created an initial PR for migrating from apollo-server-lambda to Envelop and GraphQL-Helix. The goal of this PR is to normalize the incoming HTTP requests and try to handle them in a generic way. Also, since the request is detached from the handler, we can use any GraphQL library for execution.
While
graphql-helix
provides the basic pipeline and the initial request normalization,envelop
provides the connection to the GraphQL execution, and allow to enrich the entire GraphQL execution pipeline with custom code (custom context building, parser cache, validation cache, tracing, metrics collection and more).Dotan Simha - Partial normalization of Lambda request (April 25, 2021)
The initial PR, Partial normalization of Lambda request for migration to Envelop, laid the foundation for using GraphQL-Helix and Envelop.
- GraphQL Helix is a framework and runtime agnostic collection of utility functions for building your own GraphQL HTTP server.
- Envelop is a lightweight library allowing developers to easily develop, share, collaborate and extend their GraphQL execution layer. Envelop is the missing GraphQL plugin system.
Earlier this week I released GraphQL Helix, a new JavaScript library that lets you take charge of your GraphQL server implementation.
There's a couple of factors that pushed me to roll my own GraphQL server library:
- I wanted to use bleeding-edge GraphQL features like
@defer
,@stream
and@live
directives.- I wanted to make sure I wasn't tied down to a specific framework or runtime environment.
- I wanted control over how server features like persisted queries were implemented.
- I wanted to use something other than WebSocket (i.e. SSE) for subscriptions.
Unfortunately, popular solutions like Apollo Server, express-graphql and Mercurius fell short in one or more of these regards, so here we are.
Daniel Rearden - Building a GraphQL server with GraphQL Helix (November 5, 2020)
The code for this project can be found on my GitHub.
yarn create redwood-app redwood-envelop
cd redwood-envelop
Open schema.prisma
in api/db
and add the following schema.
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
model Post {
id Int @id @default(autoincrement())
title String
body String
createdAt DateTime @default(now())
}
First you need to create a Railway account and install the Railway CLI.
railway login
railway init
railway add
Add a PostgreSQL plugin to your Railway project and then set the DATABASE_URL
inside your .env
file.
echo DATABASE_URL=`railway variables get DATABASE_URL` > .env
Running yarn rw prisma migrate dev
generates the folders and files necessary to create a new migration. We will name our migration posts-table
.
yarn rw prisma migrate dev --name posts-table
yarn rw g scaffold post
yarn rw dev
Open http://localhost:8910/posts
to create a couple blog posts.
Add useEnvelop=true
to the [experimental]
section in your redwood.toml
config. This lets the dev-server
know how to handle the response.
[web]
port = 8910
apiProxyPath = "/.redwood/functions"
[api]
port = 8911
[browser]
open = true
[experimental]
esbuild = false
useEnvelop = true
yarn workspace api add @redwoodjs/graphql-server
// api/src/lib/logger.js
import { createLogger } from '@redwoodjs/graphql-server/logger'
export const logger = createLogger({
options: { level: 'info', prettyPrint: true },
})
// api/src/functions/graphql.js
import {
createGraphQLHandler,
makeMergedSchema,
makeServices,
} from '@redwoodjs/graphql-server'
import schemas from 'src/graphql/**/*.{js,ts}'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'
import services from 'src/services/**/*.{js,ts}'
export const handler = createGraphQLHandler({
loggerConfig: {
logger,
options: {
operationName: true,
tracing: true
}
},
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
onException: () => {
db.$disconnect()
},
})
Apollo plugins are not currently supported and must be removed. However, there may be equivalent Envelop plugins. These can be added in the createGraphQLHandler
configuration options in extraPlugins
. extraPlugins
accepts an array of plugins.
'@envelop/depth-limit'
'@envelop/disable-introspection'
'@envelop/filter-operation-type'
'@envelop/parser-cache'
'@envelop/validation-cache'
'@envelop/use-masked-errors'
Change the imports to use the new graphql-server
package if your services raise any errors based on ApolloError
such as UserInputError
or ValidationError
.
import { UserInputError } from '@redwoodjs/graphql-server'
If you have any other @redwoodjs/api
imports in your project make sure to change them to @redwoodjs/graphql-server
.
yarn rw dev
yarn rw g page home /
yarn rw g cell posts
// web/src/components/PostsCell/PostsCell.js
export const QUERY = gql`
query PostsQuery {
posts {
id
title
body
createdAt
}
}
`
export const Loading = () => <div>Almost there...</div>
export const Empty = () => <div>WHERE'S THE POSTS?</div>
export const Failure = ({ error }) => <div>{error.message}</div>
export const Success = ({ posts }) => {
return posts.map((post) => (
<article key={post.id}>
<header>
<h2>{post.title}</h2>
</header>
<p>{post.body}</p>
<div>{post.createdAt}</div>
</article>
))
}
// web/src/pages/HomePage/HomePage.js
import PostsCell from 'src/components/PostsCell'
const HomePage = () => {
return (
<>
<h1>Redwood+Envelop</h1>
<PostsCell />
</>
)
}
export default HomePage
Generate the configuration file needed for deploying to Netlify with the following setup command.
yarn rw setup deploy netlify
Create a blank repository at repo.new and push the project to your GitHub.
git init
git add .
git commit -m "the guilded age of redwood"
git remote add origin https://github.com/ajcwebdev/redwood-envelop.git
git push -u origin main
Go to Netlify and connect the repo. Include the DATABASE_URL
environment variable and add ?connection_limit=1
to the end of the connection string. You can also give your site a custom domain such as redwood-envelop
.
Test the API with a query
query getPosts {
posts {
id
title
body
createdAt
}
}
RedwoodJS was originally architected around Apollo Client on the web
side and Apollo Server on the api
side. These two libraries were fundamental to the development of not only Redwood but the entire GraphQL ecosystem. Apollo itself was born from the ashes of the Meteor Development Group.
Meteor pursued a similar philosophy of fullstack JavaScript now employed by Redwood. By bringing the decoupled Apollo pieces of client and server together into a single fullstack application it felt like the original dream of Meteor was finally coming to fruition. But GraphQL is itself about decoupling the frontend from the backend so that one side is never too heavily tied to the other.
This has allowed Redwood to pursue other GraphQL clients and servers that continue to evolve and improve and engage with the open source developer community. The free market of repositories is alive and well, and we will see many more experiments in the coming future.
23