Skip to content

Project-Setup/grpc-ts

Repository files navigation

gRPC with Typescript

  • gRPC-js
  • Typescript
  • Eslint
  • Jest

Example Project

  • go to example/ folder for example project setup

Setup

Setup the project

git init
nvm install node
nvm use node
node -v > .nvmrc
npm init -y
  1. install required packages
npm i -D typescript @babel/core @babel/cli @babel/register @babel/plugin-proposal-class-properties @babel/preset-env @babel/preset-typescript @types/node @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-nullish-coalescing-operator
  1. create babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }],
    ['@babel/preset-typescript', { allowNamespaces: true }],
  ],
  plugins: [
    '@babel/proposal-class-properties',
    '@babel/plugin-proposal-optional-chaining',
    '@babel/plugin-proposal-nullish-coalescing-operator',
  ],
  comments: false,
};
  1. create ./src/index.js
require('@babel/register')({ extensions: ['.js', '.ts'] });
// import all other files after the babel hook
// const app = require('./app');
  1. add tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "noEmit": true,
    "moduleResolution": "node",
    "strict": true,
    "alwaysStrict": true,
    "typeRoots": [
      "./node_modules/@types"
    ],
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "resolveJsonModule": true,
    "removeComments": false,
    "skipLibCheck": true,
    "target": "esnext",
    "forceConsistentCasingInFileNames": true
  },
  "include": [
    "./src/**/*"
  ]
}
  1. create lib/
mkdir ./lib
  1. install nodemon for dev
npm i -D nodemon
  1. add scripts to package.json
{
  "scripts": {
    "dev": "DEV_ENV=true nodemon ./src/index.js",
    "start": "node ./lib/index.js",
    "type-check": "tsc --noEmit",
    "build": "babel src --out-dir lib --copy-files --extensions '.ts,.js'",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}
  1. install required packages
npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm i -D eslint-plugin-import
npm i -D prettier eslint-config-prettier eslint-plugin-prettier
  1. create .eslintrc.js
module.exports =  {
  parser:  '@typescript-eslint/parser',  // Specifies the ESLint parser
  extends:  [
    'plugin:@typescript-eslint/recommended',  // Uses the recommended rules from @typescript-eslint/eslint-plugin
    'prettier/@typescript-eslint',  // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
    'plugin:prettier/recommended',  // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
  ],
  parserOptions:  {
    ecmaVersion:  2018,  // Allows for the parsing of modern ECMAScript features
    sourceType:  'module',  // Allows for the use of imports
  },
  env: {
    node: true,
    jest: true
  },
  rules:  {
    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
    // e.g. '@typescript-eslint/explicit-function-return-type': 'off',
    'no-unused-vars': 'off',
    '@typescript-eslint/no-unused-vars': ['error', {
      'vars': 'all',
      'args': 'after-used',
      'ignoreRestSiblings': false
    }],
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/explicit-function-return-type': 0,
    '@typescript-eslint/no-namespace': 'off',
    'no-bitwise': 'off'
  },
  plugins: [
    '@typescript-eslint/eslint-plugin',
  ],
  settings:  {
    'import/resolver': {
      node: {
        extensions: ['.js', '.ts'],
      },
    },
  },
};
  1. create .prettierrc.js
module.exports =  {
  semi:  true,
  trailingComma:  'es5',
  singleQuote:  true,
  printWidth:  100,
  tabWidth:  2,
};
  1. install required packages
npm i -D jest babel-jest
npm i -D @types/jest
  1. add scripts in package.json
{
  "scripts": {
    "test": "tsc & jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
}
  1. create jest.config.js
module.exports = {
  moduleFileExtensions: ['ts', 'js'],
  testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js?|ts?)$',
  globals: {
    NODE_ENV: 'test',
  },
  transform: {
    '^.+\\.(j|t)s$': 'babel-jest',
  },
  coveragePathIgnorePatterns: [
    '/node_modules/',
    'jest.setup.js',
    '<rootDir>/configs/',
    'jest.config.js',
    '.json',
    '.snap',
  ],
  coverageReporters: ['json', 'lcov', 'text', 'text-summary'],
  moduleNameMapper: {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/__mocks__/mocks.js',
    '\\.(css|less|scss)$': '<rootDir>/__mocks__/mocks.js',
  },
};
  1. install required packages
npm i -S @grpc/grpc-js @grpc/proto-loader
npm i -D grpc-tools grpc_tools_node_protoc_ts
  1. create proto/
mkdir ./proto
  1. create script/build_proto.sh
#!/usr/bin/env bash

BASEDIR=$(dirname "$0")
cd ${BASEDIR}/../

PROTO_DEST=${1:-./src/proto}

mkdir -p ${PROTO_DEST}

# grpc-js
# JavaScript code generating
grpc_tools_node_protoc \
--js_out=import_style=commonjs,binary:${PROTO_DEST} \
--grpc_out=grpc_js:${PROTO_DEST} \
-I ./proto \
proto/*.proto

grpc_tools_node_protoc \
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--ts_out=grpc_js:${PROTO_DEST} \
-I ./proto \
proto/*.proto
  1. add script to package.json
"scripts": {
  "build-proto": "sh ./script/build_proto.sh"
}