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
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
- ...
Apollo Client settings. injects accessToken
in the Authorization Header in the GraphQL request.
- Implementation about refreshing the
accessToken
is required
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
).
Codes automatically generated by GraphQL Code Generator is saved.
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
.
Implementation of themes, global css styles
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.
Type declarations. (.d.ts
)
Recommendationđź‘Ź: The type used in each service unit should be stored in
~/services/{service}/types
in each service.
Entry TypeScript files for a serverless deployment.
Recommendationđź‘Ź: Separate entry files into service units
# Start development server
$ yarn dev
with Hooks API
import { useStore } from '~/store'
export default function PageIndex() {
const store = useStore()
/* ... */
}
-
Edit GraphQL Endpoint (
NEXT_APP_GRAPHQL_ENDPOINT
) in.env.development
,.env.production
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
)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
import { useGetPikachuQuery } from '~/generated/graphql' export default function Pikachu() { const { data, loading, error } = useGetPikachuQuery() /* ... */ }
-
Inject variables that used globally
import openColor from 'open-color' const theme = { ...openColor, }
-
Use the variables in
styled
statements withprops.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.
- To use environment variables in client and server both, append
NEXT_APP
in variable nameNEXT_APP_VERSION = "1.0.0"
- To use environment variables in server only by security reason, do not append
NEXT_APP
in variable nameSECRET_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
- Refactoring JS files to TypeScript
- I18n Support
If you have a feature request, please create a new issue. And also, pull requests are always welcome🙏