Currently, Redwood uses Apollo Server (via apollo-server-lambda) as its GraphQL server. With v0.35, a new @redwoodjs/graphql-server
package built upon GraphQL Helix and the Envelop plugin system is available. (Read more about Envelop here.)
This is a preview feature requiring opt-in. We need your help with testing and feedback!
Contents
- Why are we excited?
- Collaboration with the Guild
- More Choice
- Better Plugins
- Security and Best Practices
- Future Roadmap
- Benchmarks
- How to Use Redwood with GraphQL Helix and Envelop
- Let Us Know: Testing and Feedback
Why are we excited?
Collaboration with the Guild
This project has been done with close collaboration between The Guild and the Redwood Core Team. From opening the original PR to creating new plugins to advising on GraphQL best practices and security, members of The Guild like @dotansimha (github) and @n1ru4l (github) have been along for the ride every step of the way. And they are committed to the future success of this integration.
More Choice
One of the tenets of the Redwood philosophy is “Redwood believes that, as much as possible, you should be able to operate in a serverless mindset and deploy to a generic computational grid.”
To be, able to deploy to a “generic computation grid” means that as a developer you should be able to deploy using the provider or technology of your choosing. You should be able to deploy to Netlify, Vercel, Render, AWS Serverless, or elsewhere with easy and no vendor or platform lock in. You should be in control of the framework, what the response looks like and how your client consume it.
The same should be true of your GraphQL Server. GraphQL Helix aims fix that:
Existing libraries like Apollo Server provide you with either a complete HTTP server or else a middleware function that you can plug into your framework of choice. GraphQL Helix takes a different approach — it just provides a handful of functions that you can use to turn an HTTP request into a GraphQL execution result. In other words, GraphQL Helix leaves it up to you to decide how to send back the response.
Instead of closing tying a GraphQL server to a client (as is the case with Apollo), GraphQL Helix offers a solution using GraphQL over HTTP, adhering to best practices and standards that offer more options to developers.
For example, Redwood’s graphql-server simply uses the APIGatewayProxyResult
from aws-lambda
to shape the response. And that means Redwood can soon offer different ways to handle the response to support Azure, Google Cloud, Cloudfront and more.
All this gets us closer to Redwood’s goal of being able to deploy to a “generic computation grid”. And that’s exciting!
Better Plugins
Envelop describes itself as the “missing GraphQL plugin system”.
envelop
is a lightweight JavaScript (/TypeScript) library for customizing the GraphQL execution layer and flow, allowing developers to build, share and collaborate on GraphQL-related plugins while filling the missing pieces in GraphQL implementations.
envelop
aims to extend the GraphQL execution flow by adding plugins that enrich the feature set of your application
If you haven’t used GraohQL plugins before, you should check out envelop’s Plugin Hub.
These plugins can do some amazing things like validation, logging, sending analytics or errors to Sentry, and importantly making your endpoint more secure.
Because envelop provides “a standardized interface” and “hook(s) into specific phases within the GraphQL execution pipeline” you can alter each phase based on your needs. You have complete control over what your GraphQL Server can do. And it’s easy to write your own.
Plugins help keep your GraphQL api secure and follow best practices, too.
Security and Best Practices
We’ll have more to share around securing your GraphQL API in addition to securing your services in upcoming posts.
With v0.35, the GraphQL server improves security by:
- Enforcing depth limits: Attackers often submit expensive, nested queries to abuse query depth that could overload your database or expend costly resources. The GraphQL Server is now setup by default to limit depth and can be configured to your custom depth.
- Error masking: Exceptions and stack traces (such as Prisma query errors) if leaked to the client can expose sensitive information about your api, schema, and data. The GraphQL Server now masks any errors that are not specifically GraphQL related and won’t reveal the inner workings that could be exploited by attackers.
- Logging: Logging is essential in production apps to be alerted about critical errors and to be able to respond effectively to support issues. In staging and development environments, logging helps you debug queries, resolvers and cell requests. Logging also has the added benefit from being able to redact sensitive information, can keep your service concise and free of logger-bloat, and can send to transports, like DataDog…
Future Roadmap
What’s most exciting about GraphQL Helix and Envelop being part of the RedwoodJS Framework is what the future holds and the opportunities that are possible.
- First, by moving off
apollo-server-lambda
, RedwoodJS can provide a more plugin-based, configurable approach to offer different ways of shaping the GraphQL to support Azure, Google Cloud, Cloudflare, and more. - Second, one of the most asked about topics is how to implement GraphQL subscriptions in a serverless infrastructure. Now, instead of having a web-sockets solution, RedwoodJS might be able to take advantage of new ways of performing live queries with the
@live
directive. - Third, by being able to better control the shape of the GraphQL response and also adhering to more standards, supporting more sides (mobile, CLI, etc.) will be easier since they won’t have to adhere to the Apollo Server response or use the Apollo client.
- Last, RedwoodJS can have much more control over ways to filter the schema — one can remove fields from types to keep the data much more secure (ie, never send a private field with sensitive data like emails, credit card numbers, etc.) or even generate a filtered schema dedicated to a side. That means from a single SDL and schema one could define that only certain queries and mutations are available to the CLI side while others open to a public web side and even still just certain only available to authenticated users.
And of course, having The Guild onboard helps give RedwoodJS GraphQL API a bright future.
Benchmarks
Both GraphQL Helix and Envelop run performance tests and share their benchmark results.
You can see what they do to and how they measure here:
If you want to run your own benchmarks, you can enable tracing in your logger.
If you want to run your own benchmarks, you can enable tracing in your logger.
GraphQL Helix aims to be lightweight and the RedwoodJS released packages show that the graphql-server
is smaller by ~100kb, while still being backwards compatible with the api
package. These savings add up when deployed in a serverless environment and can show improved build and deploy times and also faster cold starts.
API Package: 330kb
GraphQL-Server Package: 225kb
Helix and envelop are now the default in Redwood as of v0.37+ the guide below is no longer required.
Old opt-in instructions. **No longer requried**
# How to Use Redwood with GraphQL Helix and EnvelopThis is a preview feature and should be tested in staging or non-critical contexts prior to production use.
In order to use GraphQL Helix and Envelop in a new or existing app, you will have to make the following changes.
- In your
redwood.toml
config, adduseEnvelop=true
to the[experimental]
section in order to inform the dev-server how to handle the response. For example:
[web]
port = 8910
apiProxyPath = "/.redwood/functions"
[api]
port = 8911
[browser]
open = false
[experimental]
esbuild = false
useEnvelop = true
[generate]
nestScaffoldByModel = false
- Add
@redwoodjs/graphql-server
to dependencies inapi/package.json
yarn workspace api add @redwoodjs/graphql-server
- Be sure to have a logger defined in
/api/src/lib/logger.js|ts
and update the import to use the newgraphql-server
package
import { createLogger } from '@redwoodjs/graphql-server/logger'
// ...
export const logger = createLogger({
options: { level: 'info', prettyPrint: true },
})
Note: options here are an example. If you are sending logs to DataDog or some other log transport, then you likely want prettyPrint to be false (the default in non dev environments).
- Update your
graphql.js|ts
function to use the newgraphql-server
package and add yourlogger
:
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: {}
},
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})
-
Change any
'@redwoodjs/api'
imports to@redwoodjs/graphql-server
-
Make Custom ContextFunction Changes if needed
If you use a custom ContextFunction to modify the context in the createGraphQL
handler, now the function is provided only the context
and not also the event
. However, the event
information is available as an attribute of the context as context.event
.
Therefore, your custom ContextFunction setIpAddress()
accessed the event
as in:
const ipAddress = ({ event }) => {
return (
event?.headers?.['client-ip'] ||
event?.requestContext?.identity?.sourceIp ||
'localhost'
)
}
const setIpAddress = async ({ event, context }) => {
context.ipAddress = ipAddress({ event })
}
with envelop, you would use simple get the event from the context:
const setIpAddress = async ({ context }) => {
context.ipAddress = ipAddress({ event: context.event })
}
What should I look for?
- The
graphql-server
should work equivalently and be backward compatible with the classicapi
package’s GraphQL Server. That is, “things should work just as they did before.” - Please configure logging. Do you like it? Try setting up data, operation name and query logging to get insight into your services without having do add logging in each service.
- Let us know if you use any existing plugins or validation rules and we’ll help find equivalents.
- Any issues with custom context
- Forms and form errors are one area where the client still retains some Apollo Server-like backward compatibility. If you use Forms and display errors in any custom way or access error messages, please try that out and report back any issues you may see.
Let Us Know: Testing and Feedback
We’re looking for you, as part of the RedwoodJS community, to help validate and improve the new feature.
- Please enable this GraphQL Server in your new apps and test out in developer.
- If you upgrade your old apps, please test in a Staging environment before promoting to Production once you feel confident that you are seeing your API side work as expected.
How to report and give feedback?
You can reply to this post, of course, but also you can create a New Issue and reference “graphql-server” or “helix” or “envelop” in the title.