Understanding RedwoodJS Architecture
Monorepo with API and Web Sides
RedwoodJS projects follow a monorepo pattern with clear separation between the web
and api
sides. The API side uses GraphQL with Prisma and runs in a Lambda-like environment, while the web side is a React SPA powered by Redwood's routing and cells abstraction.
project-root/ ├── api/ │ ├── src/functions/graphql.js │ ├── src/services/ │ └── ... ├── web/ │ ├── src/pages/ │ ├── src/components/ │ └── ...
GraphQL Codegen and Schema Drift
Redwood auto-generates SDL files and types using GraphQL code generation. If resolvers or Prisma models are out of sync, builds may succeed locally but fail in CI, or runtime errors may appear when deploying functions.
Common Advanced Failures
- "Cannot return null for non-nullable field" errors in cells
- Deployment mismatches due to stale GraphQL schemas or uncommitted generated files
- Unexpected Prisma client errors when using nested relations
- State leakage across web cells due to misused hooks or side effects
- Cloud provider build failures due to incompatible Node versions or missing binary targets in Prisma
Diagnostics and Tools
1. Confirm GraphQL Type Generation
yarn rw prisma generate yarn rw g types git status # ensure generated files are tracked
Type mismatches usually stem from changed models or services not followed by re-generation.
2. Debug Cells and Data Fetching
// In MyCell.js export const Loading = () =>Loading...export const Failure = ({ error }) =>Error: {error.message}export const Success = ({ data }) =>{data?.someValue}
Enable logging inside services or GraphQL resolvers to confirm what's returned versus what's expected in cells.
3. Trace Prisma and Database Errors
npx prisma studio yarn rw prisma migrate dev --name debug_migration tail -f ./api/logs/graphql.log
Ensure your schema.prisma matches the expected shape of data in GraphQL resolvers.
4. Analyze Build Artifacts in CI/CD
Redwood supports deployments to Vercel, Netlify, and custom environments. Mismatches in Node versions, missing env vars, or skipped Prisma generation steps can break builds.
# .node-version or engines field in package.json must match runtime "engines": {"node": "18.x"}
Architectural Pitfalls
1. Tight Coupling Between Cells and Services
Cells should be designed for pure data fetching. Injecting business logic or side-effects can lead to non-deterministic behavior and reduce reusability across pages.
2. Ignoring GraphQL Nullability Contracts
Redwood enforces strict nullability in GraphQL schemas. If a resolver or Prisma query returns null for a non-nullable field, GraphQL throws at runtime. Use optional chaining and null checks to validate responses.
3. Misconfiguring Webpack or Babel in Custom Deployments
Enterprise users customizing Redwood builds may override Babel/Webpack improperly, leading to tree-shaking issues or dead code paths not being eliminated. Always test builds with yarn rw build --stats
.
Step-by-Step Fixes
1. Fix Broken Cells with Schema Validation
If a Cell returns undefined or null for a required field, ensure the corresponding service or resolver handles fallbacks or filters correctly.
// Example service return db.user.findUnique({ where: { id } }) || { id: -1, name: "Unknown" }
2. Regenerate Types and Prisma Client
yarn rw prisma generate yarn rw g types git add api/types graphql.schema.json
3. Isolate Global State in Cells
Do not mutate global state or use unstable hooks in Cells. Wrap shared logic in custom hooks and move side-effects to parent components.
4. Lock Node and Prisma Versions
CI failures often stem from mismatched Prisma binary targets. Define versions explicitly:
"engines": { "node": "18.x" } "prisma": { "version": "5.3.1", "binaryTargets": ["native"] }
5. Use Environment-Specific .env Files
Redwood loads .env
, .env.defaults
, and environment-specific variants. Ensure all deployments have required variables defined.
# .env.production DATABASE_URL=postgresql://user:password@host/db API_URL=https://myapi.com
Long-Term Best Practices
- Use GraphQL codegen and lint rules in CI to prevent schema drift
- Separate business logic from GraphQL resolvers into service layers
- Document all generated types and re-generate types before every commit
- Monitor API side for performance bottlenecks and cold starts
- Adopt Redwood's experimental support for Streaming and RSC cautiously in production
Conclusion
RedwoodJS simplifies full-stack development, but complex workflows and integrations demand deeper operational discipline. Flaky cells, GraphQL inconsistencies, and CI/CD failures often trace back to schema drift, type mismatches, or stateful design patterns. By enforcing strong contracts, maintaining codegen hygiene, and architecting for separation of concerns, development teams can scale RedwoodJS projects with confidence and avoid hidden pitfalls in enterprise environments.
FAQs
1. Why do my RedwoodJS builds fail only in CI?
This usually happens due to missing environment variables, incorrect Node versions, or uncommitted generated GraphQL/Prisma types.
2. How do I prevent GraphQL "null for non-nullable field" errors?
Ensure your resolvers and Prisma queries always return fallback values or update the schema to reflect nullability correctly.
3. Can I use Apollo Client or Relay with RedwoodJS?
Redwood comes with Apollo preconfigured. Using Relay would require significant overrides and is not officially supported.
4. What's the best way to share logic across multiple cells?
Create reusable hooks or services. Avoid placing side-effects or business logic directly inside Cell success components.
5. Does RedwoodJS support serverless deployments?
Yes. Redwood's API side is optimized for serverless environments like Netlify Functions, Vercel, or custom AWS Lambda targets.