Skip to content

Latest commit

 

History

History
291 lines (241 loc) · 8.27 KB

README.md

File metadata and controls

291 lines (241 loc) · 8.27 KB

Next.js Starter using GraphQL

Production-ready Next.js boilerplate, GraphQL and MobX based. Authentication status can be stored in cookie with Secure,HttpOnly options.

Pre-Installed

The following libraries / Framework are pre-installed.

1. Getting Started

# 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

2. Folder Structure

1. /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
  • ...

2. /src/services/index/apollo

Apollo Client settings. injects accessToken in the Authorization Header in the GraphQL request.

  • Implementation about refreshing the accessToken is required

3. /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).

4. /src/generated

Codes automatically generated by GraphQL Code Generator is saved.

5. /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.

6. /src/styled

Implementation of themes, global css styles

7. /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.

8. /src/types

Type declarations. (.d.ts)

Recommendation👏: The type used in each service unit should be stored in ~/services/{service}/types in each service.

9. /serverless

Entry TypeScript files for a serverless deployment.

Recommendation👏: Separate entry files into service units

3. Development

# Start development server
$ yarn dev

Use MobX store in Components

with Hooks API

~/services/home/pages/index.tsx
import { useStore } from '~/store'

export default function PageIndex() {
  const store = useStore()

  /* ... */
}

GraphQL Query, Mutation with GraphQL Code Generator and react-apollo-hooks in Components

  • Edit 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()
    
      /* ... */
    }

Styled Components with Theme Injections

  • 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;
    `

Inject Environment variables with MobX store hydration

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 name
    .env.**
    NEXT_APP_VERSION = "1.0.0"
    
  • To use environment variables in server only by security reason, do not append NEXT_APP in variable name
    .env.**
    SECRET_KEY = "dc35abc5-80e1-5725-8a7b-7a6ce1a21c24"
    

4. Build

# Build server and client bundles
$ yarn build

5-a. Deployment on Server (EC2, Elastic Beanstalk, Docker, ...)

# Run server with bundled assets in 80 port
$ yarn start

5-b. Deployment on Serverless (AWS Lambda + API Gateway + S3)

Pre-requisites

  • 🔑 IAM Account for Serverless framework (Requires pre-configuration using aws configure)

    $ aws configure

Configuration

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

Run deployment by serverless framework

# development stage
$ yarn deploy:dev

# staging stage
$ yarn deploy:stage

# production stage
$ yarn deploy:prod

6. To-do

  • 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🙏