Introduction
Web development can be overwhelming. With countless libraries and frameworks out there, it can be hard to piece together technologies to build a complete full stack web application. Through trial and error, I've found a technology stack and development workflow for building web applications that works for me. Hopefully my setup will give you some ideas.
What we’ll cover
In the post, I’ll give you an overview of the libraries and frameworks I use. I’ll explain how I structure my projects and walk through my typical development workflow. My goal is to show you how all the pieces fit together.
What we won’t cover
I will not be going in-depth on any specific technology. Instead, I’ll provide links to more detailed documentation and articles where you can learn more. If you’re looking for a comprehensive deep dive on building full stack web applications with React, I recommend checking out Wes Bos’ Advanced React course.
Table of contents
Architecture
Frontend
- React - A JavaScript library for building user interfaces
- Next.js - A framework for building server-rendered apps with React
-
Emotion - A library designed for writing CSS styles with JavaScript
- Styled Components is another popular choice for writing CSS styles with JavaScript.
- Apollo Client - A GraphQL client and state management library for JavaScript apps
- GraphQL Code Generator - Generate code from a GraphQL schema
Backend
- Prisma - Database tools for modern application development
- Nexus - Code-First, Type-Safe, GraphQL Schema Construction
-
GraphQL Yoga - Fully-featured GraphQL Server
- Apollo Server is another popular GraphQL Server.
Deployment
-
Now - A static hosting platform
- I use Now to deploy my frontend and GraphQL API.
-
Heroku - A cloud application platform
- I use Heroku to run my Prisma Server and database.
Folder structure
Here's how I structure my projects:
frontend/ # Where all the frontend code lives
package.json # Manifest file with a list of package dependencies for frontend
next.config.js # Config file for Next.js
pages/ # Each file in this folder becomes a page with a URL based on its path
graphql/ # GraphQL queries and mutations
components/ # React components
lib/ # Miscellaneous utility functions
__generated__/ # Auto-generated React components and TypeScript types
backend/ # Where all the backend code lives
package.json # Manifest file with a list of package dependencies for backend
src/
index.ts # Entrypoint for the GraphQL server
schema.ts # Schema definition for GraphQL Server
__generated__/ # Auto-generate TypeScript types
prisma/ # Prisma datamodel and config files
Note: I use Yarn Workspaces to easily manage dependencies and run scripts for both the
frontend
andbackend
.
Workflow
To illustrate my typical development workflow, let's walk through the process of adding displaying a list of posts on our application:
1. Start the development server
To start local development, we can run yarn dev
from the root directory.
yarn dev
This will run the frontend development server on localhost:3000
and the GraphQL API server on localhost:4000
.
2. Update the datamodel
Next, in backend/prisma/datamodel.prisma
, we need to create a Post
type:
type Post {
id: ID! @id
createdAt: DateTime! @createdAt
updatedAt: DateTime! @updatedAt
author: String!
title: String!
content: String!
}
This is a special language for defining datamodels in Prisma. It’s similar to how you define GraphQL schemas.
3. Deploy the datamodel changes
Before we can do anything with our new Post type, we have to deploy our changes:
prisma deploy
This will automatically run a database migration and generate a GraphQL API that will allow us to perform CRUD options on posts. It will also generate TypeScript types that we can use in our GraphQL API.
4. Update the GraphQL API
Next, we need to update our GraphQL API to enable our frontend to display and edit posts. Since Prisma’s GraphQL API already exposes CRUD operations for posts, we can just proxy those fields. Here’s how we do that with Nexus:
const Query = prismaObjectType<'Query'>({
name: 'Query',
definition(t) {
t.prismaFields([
+ 'post',
+ 'posts',
])
},
})
const Mutation = prismaObjectType<'Mutation'>({
name: 'Mutation',
definition(t) {
t.prismaFields([
+ 'createPost',
+ 'updatePost',
+ 'deletePost',
])
}
})
5. Write a GraphQL query
We use the frontend/graphql
directory to co-locate all the queries and mutations used in the frontend. So, let’s create a file called getPosts.graphql
in this directory and write a query to get a list of posts:
query getPosts {
posts {
id
createdAt
title
content
}
}
6. Generate GraphQL types and components
Before we can use this query, we need to generate React components and TypeScript types for it. This is where GraphQL Code Generator comes in handy. With one command, we can generate React components and types for all .graphql
files in our project:
gql-gen
The generated output will be placed in frontend/__generated/graphql.tsx
.
7. Update the frontend
In any React component, we can now import the code generated by GraphQL Code Generator and use it display a list of posts:
import { GetPostsComponent } from '../__generated__/graphql'
export default function App() {
return (
<GetPostsComponent>
{({ loading, error, data }) => {
if (loading) return <p>Loading...</p>
if (error) return <p>Error: {error.message}</p>
return (
<ul>
{data.posts.map(post => (
<li key={post.id}>{post.title}</li>
)}
</ul>
)
}}
</GetPostsComponent>
)
}
8. Deploy
We've now made all the necessary frontend and backend changes to display posts. It's time to deploy our changes. With Now, we just need to run a single command:
now
Now will deploy the application to a unique URL (e.g. my-app-f9shcvmf1.now.sh
).
Demo
If you're interested in seeing this technology stack and workflow in a real project, check out https://github.com/colebemis/dasher.
Top comments (2)
I would consume the GraphQL API on the front end using react-apollo/hooks.
The pattern shown in this example use the render prop pattern, which was previously used by Apollo (and previous Context API) and leads to the 'component callback hell' which affects readability.
I think the apollo hooks approach (that leverages the new Context API) is much cleaner and more readable :)
This is brilliant Cole. Thank you so much