Next.js Starter using GraphQL, MobX (Next.js, TypeScript, Babel, Express.js, Apollo Client, React Apollo, React Apollo Hooks, GraphQL Codegen, MobX, mobx-state-tree, styled-components, next-optimized-images, Serverless Framework, AWS Lambda, Dotenv)
Production-ready Next.js
boilerplate, GraphQL
and MobX
based. Erion Classroom uses this boilerplate in production. Authentication status can be stored in cookie with Secure
,HttpOnly
options.
The following libraries / Framework are pre-installed.
# Clone the repository
$ git clone https://github.com/tonyfromundefined/next-starter
$ cd next-starter
# Flush git project
$ rm -rf .git
# Initialize your own git project
$ git init
# Install dependencies
$ yarn
# Start development server
$ yarn dev
/src/services
divide big application into several abstract services and store in a folder. (/home
, /auth
, ...)
Recommendationð: Isolate components, and also business logic in service unit folder
~/services/{service}/components/**.tsx
~/services/{service}/queries/**.graphql
~/services/{service}/helpers/**.ts
~/services/{service}/assets/**.png
~/services/{service}/types/**.ts
/src/services/index/apollo
Apollo Client settings. injects accessToken
in the Authorization Header in the GraphQL request.
accessToken
is required/src/services/index/store
One store that is globally used based on mobx-state-tree
. Because most of the cache processing is handled by the Apollo Client, the Store is only used for global status management, such as maintaining a authentication (storing jsonwebtoken
).
/src/generated
Codes automatically generated by GraphQL Code Generator is saved.
/src/pages
File-based page routing. All the aliases are handled through export {default}
, but all implementations are done inside /services
.
export { default } from '~/services/home/pages/index'
I've separated the code with service module for future scalability. Separate common elements used in page implementations such as
/queries
,/helpers
,/components
by service name https://softwareengineering.stackexchange.com/questions/338597/folder-by-type-or-folder-by-feature
If you set generatePageAliases
to true
in options.json
, this boilerplate traverse all /services/**/pages/**.tsx
and generate page aliases in /pages
automatically when yarn dev
.
/src/styled
Implementation of themes, global css styles
/src/api
You can create a sub-API based on Express.js. You can dynamically Set-Cookie
by ajax
call.
Recommendationð: Let the URL be a
* .json
to distinguish it from the REST API that returns JSON.
/src/types
Type declarations. (.d.ts
)
Recommendationð: The type used in each service unit should be stored in
~/services/{service}/types
in each service.
/serverless
Entry TypeScript files for a serverless deployment.
Recommendationð: Separate entry files into service units
# Start development server
$ yarn dev
with Hooks API
~/services/home/pages/index.tsx
import { useStore } from '~/store'
export default function PageIndex() {
const store = useStore()
/* ... */
}
react-apollo-hooks
in ComponentsEdit GraphQL Endpoint (NEXT_APP_GRAPHQL_ENDPOINT
) in .env.development
, .env.production
.env.development
NEXT_APP_STAGE = "development"
NEXT_APP_GRAPHQL_ENDPOINT = "https://graphql-pokemon.now.sh/"
NEXT_APP_VERSION = "0.0.1"
create a .graphql
file in service unit folder (~/services/{service}/queries/**.graphql
)
~/services/home/queries/getPikachu.graphql
query getPikachu {
pokemon(name: "Pikachu") {
id
number
name
}
}
read all .graphql
files in project and generate HOCs, hooks, components
$ yarn generate
Import the created hook, and utilize
~/services/home/components/Pikachu.tsx
import { useGetPikachuQuery } from '~/generated/graphql'
export default function Pikachu() {
const { data, loading, error } = useGetPikachuQuery()
/* ... */
}
Inject variables that used globally
./styled/themes/base.ts
import openColor from 'open-color'
const theme = {
...openColor,
}
Use the variables in styled
statements with props.theme
.
const Title = styled.h3`
font-size: 1.5rem;
background-color: ${(props) => props.theme.yellow[4]};
margin: .5rem 0 1rem;
border-radius: .25rem;
padding: .25rem;
`
To deploy multiple stage in a single build, it doesn't use dotenv-webpack
. (Include environment variable in webpack bundle) Instead of including variables in webpack, it inject server's environment variables in MobX state. So, server and client can use variables in server and client both.
NEXT_APP
in variable name
.env.**
NEXT_APP_VERSION = "1.0.0"
NEXT_APP
in variable name
.env.**
SECRET_KEY = "dc35abc5-80e1-5725-8a7b-7a6ce1a21c24"
# Build server and client bundles
$ yarn build
# Run server with bundled assets in 80 port
$ yarn start
ð IAM Account for Serverless framework (Requires pre-configuration using aws configure
)
$ aws configure
Edit serverless.yml
service: next-starter # 1. Edit service name
plugins:
- serverless-s3-sync
- serverless-apigw-binary
- serverless-dotenv-plugin
package:
individually: true
excludeDevDependencies: false
provider:
name: aws
runtime: nodejs10.x
stage: ${opt:stage, 'dev'}
region: us-east-1 # 2. Edit AWS region name
custom:
#######################################
# Unique ID included in resource names.
# Replace it with a random value for every first distribution.
# https://www.random.org/strings/?num=1&len=6&digits=on&loweralpha=on&unique=on&format=html&rnd=new
stackId: '0uelbz' # 3. Update Random Stack ID
#######################################
buckets:
ASSETS_BUCKET_NAME: ${self:service}-${self:custom.stackId}-${self:provider.stage}-assets
s3Sync:
- bucketName: ${self:custom.buckets.ASSETS_BUCKET_NAME}
localDir: dist
apigwBinary:
types:
- '*/*'
functions:
main:
name: ${self:service}-${self:custom.stackId}-${self:provider.stage}-main
handler: dist/serverless/bundles/main.handler
memorySize: 2048
timeout: 10
environment:
NODE_ENV: production
package:
include:
- dist/serverless/bundles/main.js
exclude:
- '**'
events:
- http:
path: /
method: any
- http:
path: /{proxy+}
method: any
- http:
path: /_next/{proxy+}
method: any
integration: http-proxy
request:
uri: https://${self:custom.buckets.ASSETS_BUCKET_NAME}.s3.${self:provider.region}.amazonaws.com/{proxy}
parameters:
paths:
proxy: true
# 4. If you implement more than 1 entry, add entries.
# hello:
# name: ${self:service}-${self:custom.stackId}-${self:provider.stage}-hello
# handler: dist/serverless/bundles/hello.handler
# memorySize: 2048
# timeout: 10
# environment:
# NODE_ENV: production
# package:
# include:
# - dist/serverless/bundles/hello.js
# exclude:
# - '**'
# events:
# - http:
# path: /
# method: any
# - http:
# path: /{proxy+}
# method: any
# development stage
$ yarn deploy:dev
# staging stage
$ yarn deploy:stage
# production stage
$ yarn deploy:prod
If you have a feature request, please create a new issue. And also, pull requests are always welcomeð