Nowadays, speed and productivity are the key factors in beating the competition when it comes to app and web development. This is why full-stack frameworks have gained popularity, providing devs with a solid base to efficiently implement projects that require both frontend and backend development.
In this article, we will be comparing two full-stack React frameworks: create-t3-app and RedwoodJS.
The former is a CLI for full-stack Next.js projects, while the latter is a full-stack React framework with an integrated CLI. Both have open-source codebases on GitHub, meaning anyone can explore the code and contribute.
We will take a look at the main technologies they are based on, learn how to set them up, explore the internal build structure for both, see how to work with frontend and backend development, and review what API solutions they support.
Let’s get started.
Jump ahead:
Overview
create-t3-app
One of the main cornerstones of create-t3-stack – besides simplicity and modularity – is type safety, therefore the project requires users to use TypeScript.
The project is built on top of Next.js, so if you are familiar with its file structure and features, it will be easier to get started with create-t3-app, or T3 Stack.
The recommended styling solution is Tailwind, which is a utility-first framework based on concise classes that can be efficiently composed directly in the markup.
Prisma is suggested as a database client, while tRPC is offered as the core solution for API calls. The user can also use NextAuth to speed up the authentication for the project.
The original creator of T3 Stack itself is Theo, and the create-t3-app CLI tool was made by nexxeln. If you want an indication of the popularity of this project, look no further than its progress towards the end of 2022:
Impressive, right?
RedwoodJS
The typed syntax for RedwoodJS is optional. The user has a chance to bootstrap the project either with Vanilla JS or TypeScript by adding the --ts
flag in the installation command.
RedwoodJS follows the patterns of React and is not further based on any other framework – the styling libraries can be optionally added with a single terminal command.
Instead of using tRPC, RedwoodJS uses GraphQL for its API solution. Similarly, Prisma is used as a database client.
On top of that, users can utilize Storybook as a UI component catalog, test all of the code paths with Jest, and do logging with Pino.
RedwoodJS was founded by Tom Preston-Werner, Peter Pistorius, Rob Cameron, and David Price. It has grown steadily since 2020, reaching 14K+ stars in about 3 years:
Setup
create-t3-app
To set up a create-t3-app project, users can use either npx, yarn, or pnpm by running the following commands in the terminal, respectively:
lang=bash npx create-t3-app@latest yarn create t3-app pnpm dlx create-t3-app@latest
For the purpose of this article, we will use npx. Run npx create-t3-app@latest t3app
in your terminal.
That will take you through a simple terminal wizard, asking you to configure the project. We will select all the available technologies in the wizard, which are; Prisma, tRCP, NextAuth, and Tailwind.
Once the setup is complete, change the working directory to the newly created project folder by cd t3app
, then push the Prisma database instance via npx prisma db push
and run npm run dev
to start the development server.
To preview the application, open your browser, paste http://localhost:3000 in the URL bar, and execute. You should be presented with this:
RedwoodJS
Setting up RedwoodJS is equally simple, although there are some differing nuances. For RedwoodJS, yarn is a requirement, there is no interactive terminal wizard, and by default the app runs on a different port.
Run yarn create redwood-app --ts ./redwoodapp
to start the setup:
Once the installation is complete, change the working directory into the newly created folder via cd ./redwoodapp
and run yarn rw dev
to start the development server.
It should bring up your default browser and present you with the RedwoodJS welcome page. If this does not happen, enter http://localhost:8910 in your browser URL and execute:
File structure
create-t3-app
At the root level, both the frontend and backend live in the src
folder. The only things that are separated are the database schema in the prisma
folder and the public
assets like favicon, images, audio, and related files.
The src
folder is further divided into six subfolders: env
, pages
, server
, styles
, types
, and utils
.
- The
env
folder contains the code to ensure the app is built with valid env vars pages
is used to create new pages and API endpointsserver
holds the information about the database client and the router configurationstyles
is for the external stylesheet filestypes
is for NextAuth type definitionsutils
is for additional tRCP configuration
The overall schema of the file system should look like this:
RedwoodJS
In RedwoodJS, the frontend is fully separated from the backend. All the frontend code is stored in the web
folder, while the backend code is in the api
folder.
The frontend is then divided further into the public
folder, which holds all the public assets for the app, and the src
folder, which is further divided into components
, layouts
, and pages
that allow the user to create individual components and common layouts and import them into the pages.
The rest of the individual files in the src
folder are meant to implement the root-level logic of the application, style it and provide the routing between pages.
The backend is further divided into:
- A
db
folder for database schema - A
dist
folder for compiled code - A
types
folder for compiled GraphQL types - The
src
folder
The src
folder is then further divided into directives
for GraphQL schema directives:
functions
for lambda functions generated by Redwoodgraphql
for GraphQL schemalib
for configuring auth, database, and loggingservices
for business logic related to your data
The overall schema of the file system looks as follows:
Frontend
create-t3-app
Since create-t3-app is based on the Next.js file structure; all the pages are stored as a separate file in the pages
folder.
Once you give the file a name, it automatically becomes a route. For navigating between pages, you can use the built-in Link
component.
To create a new page test
that links back to the home, you can create test.tsx
in the src
folder and include the following code:
lang=typescript import type { NextPage } from "next"; import Link from "next/link"; const Test: NextPage = () => { return ( <> <ul className="flex"> <li className="mr-6"> <Link href="/"> <a>Back to home</a> </Link> </li> <li className="mr-6"> <Link href="/test"> <a>Test</a> </Link> </li> </ul> <p>This is a test page</p> </> ); }; export default Test;
We chose to install Tailwind in the setup wizard and already used it in the code snippet above to configure the flex layout for navigation.
In order to preview the result, open your browser and navigate to http://localhost:3000/test.
RedwoodJS
To create a new page in RedwoodJS, it is recommended to use their scaffold command yarn redwood generate page test
, where test
would be your desired page title and route.
It will automatically create a new file web/src/pages/Testpage/Testpage.tsx
with the following code:
lang=typescript import { Link, routes } from '@redwoodjs/router' import { MetaTags } from '@redwoodjs/web' const TestPage = () => { return ( <> <MetaTags title="Test" description="Test page" /> <p> Find me in <code>./web/src/pages/TestPage/TestPage.tsx</code> </p> <p> This route is named <code>test</code>, link to me via ` <Link to={routes.test()}>Test</Link>` </p> <p> The link to home `<Link to={routes.home()}>Back to Home</Link>` </p> </> ) } export default TestPage
Notice that the routing is imported from @redwoodjs/router
and then the path name is used as a method on the routes. For the home route, you would first create a new page via yarn redwood generate page home /
and then use the routes.home()
method.
The scaffold command will also populate the web/src/pages/TestPage
with two additional files: TestPage.test.tsx
for testing purposes and TestPage.stories.tsx
for allowing you to work with Storybook.
If you would like to style the page with Tailwind, you would first need to run yarn rw setup ui tailwind
and then use it as in any other React app.
In order to preview the result, open your browser and navigate to http://localhost:8910/test.
Database
create-t3-app
The suggested way of handling the communication between the app and the database is via the Prisma client.
To do this, create-t3-app already has a bootstrapped scheme, which is available in prisma/schema.prisma
. For the purpose of this tutorial, open it and include the code:
lang=prisma scheme language (PSL) generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model Post { id Int @id @default(autoincrement()) title String? }
To save any changes in the Prisma schema, run a migration using the command npx prisma migrate dev --name init
. The --name
flag allows you to assign the migration name right from the terminal command.
That will create the migrations
folder inside the prisma
directory, including the migrations file with SQL commands to update the database.
After the migration is complete, generate the Prisma client via npx prisma generate
.
Now, you can view the database in Prisma studio. You can access it by running npx prisma studio
, which will start it on http://localhost:5555.
RedwoodJS
Redwood uses Prisma as the ORM as well, so it follows a similar structure for the scheme.
The only difference is that in RedwoodJS it is located at api/db/schema.prisma
. For the purpose of this tutorial, open it and include the code:
lang=prisma scheme language (PSL) datasource db { provider = "sqlite" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" binaryTargets = "native" } model Post { id Int @id @default(autoincrement()) title String? }
To migrate the changes in the schema, you have to run yarn rw prisma migrate dev --name init
. It will create a new path, api/db/migrations
, and include the SQL file with queries to update the database according to the schema.
Similarly, you should be able to access the Prisma studio. Run yarn rw prisma studio
and it should start it on http://localhost:5555 if the port is available.
Server side
create-t3-app
The communication with the backend in create-t3-app is provided via tRPC. To give you an idea of how it works, we will create a simple read functionality for the posts in the database.
First, open the Prima studio and create a few records in the Post
table so we have some sample data to work with:
Then, create a new file /src/server/router/post.ts
, initialize a new postRouter
instance, use the query()
method to add an all
endpoint to the router, and query all the posts via Prisma’s findMany()
method:
lang=typescript import { prisma } from "../db/client"; import { createRouter } from "./context"; export const postRouter = createRouter().query("all", { async resolve() { return prisma.post.findMany(); }, });
After that, import the postRouter
in /src/server/router/index.ts
and merge all routers into a single appRouter
via merge()
method:
lang=typescript import { createRouter } from "./context"; import superjson from "superjson"; import { postRouter } from "./post"; export const appRouter = createRouter() .transformer(superjson) .merge("post.", postRouter); export type AppRouter = typeof appRouter;
To display the posts, use the useQuery()
method and access the all
endpoint of the posts, and then map through the received data. To achieve this, change the /src/pages/index.tsx
to the following code:
lang=typescript import type { NextPage } from "next"; import { trpc } from "../utils/trpc"; const Home: NextPage = () => { const { data, isLoading } = trpc.useQuery(["post.all"]); if (isLoading) { return <p>Loading...</p>; } return ( <div> {data?.map((post, index: number) => { return <p key={index}>{post.title}</p>; })} </div> ); }; export default Home;
In order to preview the result, open your browser and navigate to http://localhost:3000.
We can also make use of the api
folder in the pages
directory.
Create a new file, /src/pages/api/posts.ts
and include the following code, which will create an API endpoint on posts
and fetch all the posts:
lang=typescript import type { NextApiRequest, NextApiResponse } from "next"; import { prisma } from "../../server/db/client"; const posts = async (req: NextApiRequest, res: NextApiResponse) => { const posts = await prisma.post.findMany(); res.status(200).json(posts); }; export default posts;
Now, open your browser and navigate to http://localhost:3000/api/posts, and you should be able to retrieve the data in the JSON format:
RedwoodJS
RedwoodJS offers a scaffold command to create a basic CRUD operation boilerplate with the dedicated route.
To do this, run yarn rw g scaffold post
. The post
is the name of the route – make sure you have the corresponding model with the same name in the Prisma schema file.
After that, navigate to http://localhost:8910/posts and you should see a page with create, read, update, and delete functionality.
Make sure to add some records and then view the first entry separately. You should be taken to http://localhost:8910/posts/1.
To understand how the backend works, we will take a closer look at how the data was fetched from the database.
First, by running the scaffold command in the beginning, a new file web/src/components/PostCell/PostCell.tsx
was created that uses GraphQL to fetch the data:
lang=typescript import type { FindPostById } from 'types/graphql' import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web' import Post from 'src/components/Post/Post' export const QUERY = gql` query FindPostById($id: Int!) { post: post(id: $id) { id title } } ` export const Loading = () => <div>Loading...</div> export const Empty = () => <div>Post not found</div> export const Failure = ({ error }: CellFailureProps) => ( <div className="rw-cell-error">{error?.message}</div> ) export const Success = ({ post }: CellSuccessProps<FindPostById>) => { return <Post post={post} /> }
Then the component was imported in the web/src/pages/post/PostPage/PostPage.tsx
and the specific post ID from the URL got passed to the GraphQL as a prop to query the particular post:
lang=typescript import PostCell from 'src/components/Post/PostCell' type PostPageProps = { id: number } const PostPage = ({ id }: PostPageProps) => { return <PostCell id={id} /> } export default PostPage
To test it out, try to switch between http://localhost:8910/posts/1 to http://localhost:8910/posts/2 and notice the changes.
Conclusion
Create-t3-app is built on top of Next.js, meaning the developers get a heavily optimized environment to work with. By setting TypeScript as a requirement, create-t3-app also makes sure it will be easier to detect errors in the app. In Redwood, the typed syntax is optional, by comparison.
Both are easy to set up, though create-t3-app offers more flexibility to configure what technologies the user wants to include through the setup wizard. In comparison, in RedwoodJS, the user is required to work with GraphQL, and Storybook is already integrated.
If you are a fan of scaffolding, RedwoodJS could help to speed up the development process when there are lots of pages, layouts, components, and routes to create. It allows users to create basic CRUD functionality quickly.
Overall, I would recommend crete-t3-app for those that are strong advocates of TypeScript, love to work with Next.js, and want more flexibility.
For those seeking optional Vanilla JS support, prefer to work with GraphQL, and need quick scaffolding, RedwoodJS could be a good fit for your projects.
The post create-t3-app vs. RedwoodJS: Comparing full-stack React frameworks appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/06THQV8
Gain $200 in a week
via Read more