Today we’re announcing Redwood v3.0.0!
The original motivation for this major release was to drop support for Node.js v12. Many NPM packages and deployment providers that Redwood depends on and supports dropped support for Node.js v12, so it was time for us to do it too.
At the same time, many of Redwood’s fundamental dependencies, like Fastify, Jest, and Prisma, released a major version of their own, so it made a major even more fitting.
Finally, there are a few features we want to ship that have breaking changes. They were waiting for a major to come around to land, so we’re happy to ship them today! In this release you can also look forward to Cell and route parameter prerendering, TypeScript Strict Mode, WebAuthn Support, and more.
We’ve tried to make the upgrade path as smooth as possible. See the How to Upgrade section below for step-by-step instructions. And join us for a live stream on all the new features in this release (the exact date is still TBD, but in the coming weeks):
v3.0.0 Highlights
- Cell and Route-parameter Prerendering
- TypeScript Strict Mode
- Fastify v4 and Configuring the Fastify Instance
- Jest v29 and Improvements in Test Memory Usage and Performance
- Prisma v4
- WebAuthn Support
- CLI Performance
Cell and Route-parameter Prerendering
3.0 features a major new capability to Redwood’s prerendering: Cells and routes with parameters can now be prerendered! Given a set of route parameters, Redwood can now fetch data from your GraphQL API at build-time. This means you can use Redwood as a proper static site generator. You can read more in the new Dynamic routes and Route Hooks and Cell prerendering sections of the prerender docs.
TypeScript Strict Mode
While Redwood has supported TypeScript for a while, if you’ve ever turned on strict mode before, you may have been overwhelmed by red squiggles. @danny and @Tobbe have spent a ton of time this release making sure that the TypeScript strict-mode experience in Redwood is red-squiggle free. And even if you’re not using strict mode, there’s still a lot to look forward to as they’ve improved the types across the board, especially for resolvers (i.e. your service functions). Lastly, no feature is complete without docs. We revamped the one on TypeScript.
Fastify v4 and Configuring the Fastify Instance
Previously, the api side’s server.config.js
file only exposed a limited number of Fastify options. But you told us that being able to configure the Fastify instance would be really useful. The server.config.js
file now exposes a hook to configure plug-ins on a per-side basis.
We’ve also upgraded Fastify to v4. Check out the announcement post for all the details:
Jest v29 and Improvements in Test Memory Usage and Performance
Redwood v3 upgrades Jest from v27 to v29. v28 and v29 bring a lot of new features and fixes such as sharding. Check out the blog posts for all the details:
- Jest 28: Shedding weight and improving compatibility 🫶 · Jest
- Jest 29: Snapshot format changes · Jest
We also made it a point to reduce the amount of memory tests use. We want to make sure Redwood scales so we’ll continue to refine configs like these and to ensure they’re optimal now that they’re established.
Prisma v4
Redwood 3.0 bumps the version of Prisma from v3.15.2 to v4.3.1. Prisma v4 features new Prisma Client APIs, improvements to the Prisma Schema, and the general availability of several preview features. See the release notes for all the details:
- Release 4.0.0 · prisma/prisma · GitHub
- Release 4.1.0 · prisma/prisma · GitHub
- Release 4.2.0 · prisma/prisma · GitHub
- Release 4.3.0 · prisma/prisma · GitHub
WebAuthn Support
Did you know that websites can make use of TouchID/FaceID/biometric devices, not just apps? We didn’t, until a couple of months ago! The experience is so nice that we added support for it to dbAuth. Now when a user comes to your site you can log them in the first time with username/password, then ask if they want to use TouchID/FaceID going forward. If they do, when they return to the site they can just scan their fingerprint to log in.
WebAuthn adds a new cookie, which has a separate expiration date from the regular session cookie. So you can set the session cookie to expire in, say, a day, but keep the WebAuthn cookie around for two weeks. If the user comes back after 24 hours they simply scan their fingerprint again. If they haven’t been to the site in two weeks then they’ll need to use their username/password.
If you’re starting a new app, you’ll be prompted during yarn rw setup auth dbAuth and yarn rw generate dbAuth asking if you want to enable WebAuthn support. If you do, just hit y and follow the post-install instructions.
If you have an existing app, follow the exhaustive guide here for instructions on adding support to your current codebase. If you haven’t made a ton of changes to api/src/functions/auth.js or the Login/Signup pages, it may be easier to just run the setup and generate commands again with the --force option, then checking the diff of the changes to see what was added. Then just copy/paste your custom code back into the re-generated files.
CLI Performance
The Redwood CLI can be painfully slow. There’s a lot we can do to improve performance, and we plan to do it. This release, we finished some preliminary work that should pave the way for real gains in the future. While it was just preliminary, you should experience faster startup times for the CLI overall already.
Changelog
View the complete Changelog on GitHub:
How to Upgrade
We’ve tried to make this upgrade as smooth as possible. Take a look at the breaking changes below one by one. Where possible we’ve included codemods. Odds are that not all of the changes below will be breaking for you, but we’ve taken care to document edge cases that may be.
Breaking Changes
- Cell and Route-parameter Prerendering
- TypeScript Strict Mode
- Fastify v4 and Configuring the Fastify Instance
- Jest v29
- Prisma v4
- Targeting Node.js v14
- Fixing the import-order rule
Cell and Route-parameter Prerendering
This is more of a feature than a breaking change, but there’ s a few edge cases where it could be breaking
v3 features a major new capability to Redwood’s prerendering: Cells and routes with parameters can now be prerendered! Given a set of route parameters, Redwood can now fetch data from your GraphQL API at build-time. This means you can use Redwood as a proper static site generator!
Before, Redwood couldn’t prerender routes with parameters:
<Route path="/blog-post/{id}" page={BlogPostPage} name="blogPost" prerender />
Here, the value of id
isn’t known at build-time. Before Redwood would just silently ignore this route (even though it has the prerender
prop). But now you can tell Redwood what id
s to prerender by creating a BlogPostPage.routeHooks.{js,ts}
file next to BlogPostPage.{js,ts}
:
// BlogPostPage.routesHooks.{js,ts}
export function routeParameters() {
return [{ id: 1 }, { id: 2 }, { id: 3 }]
}
In these *.routeHooks.{js,ts}
files, you have full access to your project’s api side—the database (via Prisma), services—should you need it. Just import { db } from '$api/src/lib/db'
. If you’re using TypeScript, your project’s web-side tsconfig will need a small edit to know about this path. We wrote a codemod to make this change for you:
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary tsconfig-for-route-hooks
That’s the gist of it for route parameters. You can read more in the new Dynamic routes and Route Hooks section of the prerender docs. To prerender Cells, you don’t need any special config, but you should note that prerendering always happens as an unauthenticated user. The new Cell prerendering section in the docs goes into more detail.
All together, while this feature probably won’t break your project, it could for one of the following reasons:
- if you were previously prerendering pages with route parameters, they used to just be silently ignored. Now they’ll fail and tell you that you need to create a
routeHooks
file. You can either implemented a routeHooks file, or addprerender={false}
to the route you wish to skip. - if you were previously prerendering pages that had Cells that need auth. Redwood used to just render the Loading component, but now it tries to execute the query and will error out
- your project already has a file that ends in
.routeHooks.{js,ts}
along side the page file that exports arouteParameters
function (this is pretty unlikely)
TypeScript Strict Mode
This change only affects TypeScript users. There’s at least one change you’ll have to make (documented below) whether or not you’re using strict mode.
While Redwood has supported TypeScript for a while, if you’ve ever turned on strict mode before, you may have been overwhelmed by red squiggles. @danny and @Tobbe have spent a ton of time this release making sure that the TypeScript strict-mode experience in Redwood is red-squiggle free. And even if you’re not using strict mode, there’s still a lot to look forward to as they’ve improved the types across the board, especially for resolvers (i.e. your service functions).
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary update-resolver-types
Open to see manual steps
Let’s say you have a Post service, and Posts have an Author. At the bottom of the Posts service file, we usually define the “relation” or field resolvers. We have better types for those resolvers now.
In essence PostResolvers
becomes PostRelationResolvers
:
// services/posts.ts
export const post: QueryResolvers['post'] = /**/
export const createPost: MutationResolvers['createPost'] = /**/
// 👇 these are your "relation" or field resolvers
- export const Post: PostResolvers = {
+ export const Post: PostRelationResolvers = {
author: (_obj, gqlArgs) => /**/
}
If you’re actually going to enable strict mode, there’s a few manual changes you’ll have to make after setting strict
to true. Start here and follow along: TypeScript Strict Mode | RedwoodJS Docs.
Remember to regenerate your types by running
yarn rw g types
at the end of your upgrade!
Fastify v4 and Configuring the Fastify Instance
This change affects all users and can be applied via a codemod, or by manually updating the
api/server.config.js
file
Previously, the api side’s server.config.js
file only exposed a limited number of Fastify options. But you told us that being able to configure the Fastify instance would be really useful. The server.config.js
file now exposes a hook to configure plug-ins on a per-side basis.
You’ll need to update your api side’s server.config.js
either via a codemod or manually.
To do so via codemod:
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary configure-fastify
If you choose to update manually, you’ll need to replace the api/server.config.js
with the latest version found here. Then, update the following config
to include any FastifyServerOptions
that your prior configuration used (such as a custom requestTimeout
value, etc):
/** @type {import('fastify').FastifyServerOptions} */
const config = {
requestTimeout: 15_000, // 👈 update these config settings to match your prior config
logger: {
// Note: If running locally using `yarn rw serve` you may want to adust
// the default non-development level to `info`
level: process.env.NODE_ENV === 'development' ? 'debug' : 'warn',
},
}
Want to see an example of what’s possible now? Expand the “Configuring the Fastify Instance” section below and follow along:
Configuring the Fastify Instance
For example, here you can register the compress
and rate-limit
Fastify plug-ins that will deflate or gzip your app’s responses and also enforce how many requests can be made to the api over a certain period of time. And, on the web-side, responses will have HTTP ETags generated.
Warning
This configuration does not apply in a serverless deploy, but will during local development
// api/server.config.js
/** @type {import('@redwoodjs/api-server/dist/fastify').FastifySideConfigFn} */
const configureFastify = async (fastify, options) => {
if (options.side === 'api') {
fastify.log.info({ custom: { options } }, 'Configuring api side')
await fastify.register(import('@fastify/compress'), {
global: true,
threshold: 1_024,
encodings: ['deflate', 'gzip'],
})
await fastify.register(import('@fastify/rate-limit'), {
max: 100,
timeWindow: '5 minutes',
})
}
if (options.side === 'web') {
fastify.log.info({ custom: { options } }, 'Configuring web side')
fastify.register(import('@fastify/etag'))
}
return fastify
}
For complete configuration instructions, please see Register Custom Fastify Plug-ins.
Jest v29
This change affects all users. The least you have to do is update snapshots (
yarn rw test -u
), but there may be more—read on
Redwood v3 upgrades Jest from v27 to v29. A breaking change introduced in v29 likely to affect all users is the change to the snapshot format. Jest changed the snapshot format making them more readable and copy-pasteable.
This should be an easy fix. Just update the snapshots! We recommend keeping your git history clean by doing this in its own commit:
# Provided your working directory is clean
yarn rw test -u
# Scan the changes; make sure everything looks ok
git add .
git commit -m "chore: update snapshot format"
Do you prefer the older format?
If you’re a fan of the older snapshot format, you’re free to undo the changes. All you have to do is add this to the jest.setup.js
config file in the api and web side of your project:
// In `api/jest.config.js` and `web/jest.config.js`
const config = {
rootDir: '../',
preset: '@redwoodjs/testing/config/jest/api', // or web
+ snapshotFormat: {
+ escapeString: true,
+ printBasicPrototype: true,
+ }
}
module.exports = config
The rest of the breaking changes depend on your config and dependencies. The things that are most likely to affect you are:
- a configuration option was renamed (see https://jestjs.io/docs/upgrading-to-jest28#configuration-options)
- dependencies aren’t being resolved correctly (most likely in web-side tests)
If you think a dependency isn’t being resolved correctly (the error will probably be “Jest encountered an unexpected token”), expand the section below and follow along:
Did Jest encounter an unexpected token?
If Jest isn’t resolving a dependency correctly, your tests will fail with an error like this:
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
C:\repos\accessibility-insights-web\node_modules\uuid\dist\esm-browser\index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { default as v1 } from './v1.js';
^^^^^^
SyntaxError: Unexpected token 'export'
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
> 3 | import { v4 } from 'uuid';
| ^
4 |
5 | export type UUIDGenerator = () => string;
6 |
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1773:14)
at Object.<anonymous> (src/common/uid-generator.ts:3:26)
This may seem impossible to fix, but it’s actually somewhat straightforward:
- as a simple debugging step, try rebuilding your project’s
yarn.lock
file (sometimes that’s all it takes) - if you think it’s a problem with one of the Redwood framework’s dependencies (you could figure this out using
yarn why
), make an issue - if it’s a dependency that’s specific to your project, follow one of the strategies in this GitHub issue comment
That comment is long. To save you some time, we chose the resolver strategy, and if you do too, just copy our resolver and a check for your package:
// See https://github.com/redwoodjs/redwood/blob/main/packages/testing/config/jest/web/resolver.js
module.exports = (path, options) => {
return options.defaultResolver(path, {
...options,
packageFilter: (pkg) => {
+ if (pkg.name === 'uuid' || pkg.name === 'my-troublesome-dependency') {
delete pkg['exports']
delete pkg['module']
}
return pkg
},
})
}
This file should live next to the api or web side’s—whichever one has failing tests—jest.config.js
. You can name it whatever you want, just be sure to specify it in jest.config.js
:
// In `api/jest.config.js` and `web/jest.config.js`
const config = {
rootDir: '../',
preset: '@redwoodjs/testing/config/jest/api', // or web
+ resolver: path.resolve(__dirname, './my-custom-resolver.js'),
}
module.exports = config
If that still doesn’t work, definitely make an issue!
Prisma v4
This change affects all users, but there may be nothing actionable. Read on
Redwood v3 bumps Prisma from v3.15.2 to v4.3.1. Prisma v4 was breaking for both the framework and for projects because it dropped Node.js 12 support. But there are some changes that only break the framework and some that may (depending on your use of Prisma) only break projects.
On the framework’s side, v4 was breaking because it renamed @prisma/sdk
to prisma/internals
to better communicate that the package won’t be versioned with SemVer. (So if you happen to be using it in your project for some reason, note that every release could be breaking.)
On a project’s side, it depends on your use of Prisma. So there may be no breaking changes for you, but to know for sure, you’ll have to read through Prisma’s upgrade guide. We think the sections that are most likely to affect you are…
- Explicit
@unique
constraints on one-to-one relations - Enforced use of
@unique
or@id
attribute for one-to-one and one-to-many relations (MySQL and MongoDB) - Disallow
references
syntax for implicit many-to-many relations
As with all Prisma upgrades, remember to regenerate your Prisma client after upgrading:
yarn rw prisma generate
Targeting Node.js v14
This change affects all users. Wherever you’re deploying, make sure your version of Node.js is at least 14
Redwood apps now target Node.js v14 instead of v12. (This is one of the main reason we’re releasing a major as the rest of the ecosystem has decided that it’s time to deprecate Node.js v12.) Since we use Babel (and on the api-side, ESBuild) to transpile modern JS, you’ve been able to write code for Node.js 12+ all along, so you shouldn’t have to worry about this change. But do check your deployment provider. Make sure the version of Node.js your deployed code is running on is at least v14.
Fixing the import-order rule
This change affects all users and can be applied via
yarn rw lint --fix
Last major, we added an ESLint rule to Redwood projects that orders imports. (Read more about it here https://community.redwoodjs.com/t/redwood-v2-0-0-is-now-available/3299#linting-import-order-3.)
It turns out we overlooked one thing in the api/src/functions/graphql.js
file. Those “glob star” imports are special (Babel “expands” them as a part of the build step), and we want to highlight that by setting them apart:
Actual behavior (two groups) | Desired behavior (three groups) |
---|---|
|
|
So this major includes a fix to the import-order rule. That should be the only change, and it should be as easy as last time:
# After stashing, or at least when your working directory is clean
yarn rw lint --fix
git add .
git commit -m "style: fix graphql.js imports"
We know these kinds of changes can be annoying, and don’t take changes to linting or formatting lightly. We don’t want to bikeshed, and want to do less not more. Let us know what you think!
Want to turn this off? You can, and it’s easy. Add this to the package.json
in the root of your project:
...
"eslintConfig": {
"extends": "@redwoodjs/eslint-config",
"root": true
+ "rules": {
+ "import/order": "off"
+ }
},
...