Giter Club home page Giter Club logo

nestjs-graphql-typeorm-postgresql's Introduction

NestJS/TypeORM/GraphQL/PostgresQL

NestJS boilerplate with TypeORM, GraphQL and PostgreSQL

Open for Contribution

Totally open for any Pull Request, please feel free to contribute in any ways. There can be errors related with type or something. It would be very helpful to me for you to fix these errors.

Base NestJS, We like it

We use Nestjs/TypeORM In this template, We've been trying not to use Pure SQL to make the most of TypeORM.

We use postgresQL for backend database, The default database taht will be used is named 'postgres' You have to have postgresql Database server before getting started. You can use Docker postgresQL to have server easily

packages: graphql, apollo-server-express and @nestjs/graphql, graphqlUpload ...

We use GraphQL in a Code First approach (our code will create the GraphQL Schemas).

We don't use swagger now, but you can use this if you want to. You can see playground

We use apollographql as playground. but if you want to use default playground, you can do like below.

// setting.service.ts

GraphQLModule.forRootAsync <
  ApolloDriverConfig >
  {
    ...
    useFactory: (configService: ConfigService) => ({
      ...
      playground: true,
      ...
    }),
    ...
  };

Protected queries/mutation by user role with guard

Some of the GraphQL queries are protected by a NestJS Guard (GraphqlPassportAuthGuard) and requires you to be authenticated (and some also requires to have the Admin role). You can solve them with Sending JWT token in Http Header with the Authorization.

# Http Header
{
  "Authorization": "Bearer TOKEN"
}

Example of some protected GraphQL

  • getMe (must be authenticated)
  • All methods generated by the generator (must be authenticated and must be admin)

License

MIT

Custom CRUD

To make most of GraphQL's advantage, We created its own api, such as GetMany or GetOne. We tried to make it as comfortable as possible, but if you find any mistakes or improvements, please point them out or promote them.

You can see detail in folder /src/common/graphql files

// query
query($input:GetManyInput) {
  getManyPlaces(input:$input){
    data{
      id
      logitude
      count
    }
  }
}
// variables
{
  input: {
    pagination: {
      size: 10,
      page: 0, // Started from 0
    },
    order: { id: 'DESC' },
    dataType: 'data', //all or count or data - default: all
    where: {
      id: 3,
    },
  },
};

You can see detail here.

Code generator

There is CRUD Generator in NestJS. In this repository, It has its own generator with plopjs. You can use like below.

$ yarn g

Getting Started

Installation

Before you start, make sure you have a recent version of NodeJS environment >=14.0 with NPM 6 or Yarn.

The first thing you will need is to install NestJS CLI.

$ yarn -g @nestjs/cli

And do install the dependencies

$ yarn install # or npm install

Run

for development

$ yarn dev # or npm run dev

for production

$ yarn build # or npm run build
$ yarn start # or npm run start

or run with docker following below

Docker

Docker-compose installation

Download docker from Official website

Before getting started

Before running Docker, you need to create an env file named .production.env. The content should be modified based on .example.env. The crucial point is that DB_HOST must be set to 'postgres'.

Run

Open terminal and navigate to project directory and run the following command.

# Only for prduction
$ docker-compose --env-file ./.production.env up

Note

If you want to use docker, you have to set DB_HOST in .production.env to be postgres. The default set is postgres

Only database

You can just create postgresql by below code, sync with .development.env

$ docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=1q2w3e4r -d postgres

TDD

Introduction

@nestjs/testing = supertest + jest

Before getting started

Before starting the test, you need to set at least jwt-related environment variables in an env file named .test.env.

Unit Test (Use mock)

Unit test(with jest mock) for services & resolvers (*.service.spec.ts & *.resolver.spec.ts)

Run

$ yarn test

Integration Test (Use in-memory DB)

Integration test(with pg-mem) for modules (*.module.spec.ts)

Run

$ yarn test

End To End Test (Use docker)

E2E Test(with docker container)

Run

$ yarn test:e2e:docker

CI

Github actions

To ensure github actions execution, please set the 'ENV' variable within your github actions secrets as your .test.env configuration.

Note: Github Actions does not recognize newline characters. Therefore, you must remove any newline characters from each environment variable value in your .env file, ensuring that the entire content is on a single line when setting the Secret. If you need to use an environment variable value that includes newline characters, encode the value using Base64 and store it in the Github Secret, then decode it within the workflow.

ex)

JWT_PRIVATE_KEY= -----BEGIN RSA PRIVATE KEY-----...MIIEogIBAAKCAQBZ...-----END RSA PRIVATE KEY-----

Before getting started

$ yarn prepare

Pre commit

You can check detail here

Before commit, The pre-commit hooks is executed.

Lint checks have been automated to run before a commit is made.

If you want to add test before commit actions, you can add follow line in pre-commit file.

...
yarn test
...

Pre push

You can check detail here

The pre-push hooks is executed before the push action.

The default rule set in the pre-push hook is to prevent direct pushes to the main branch.

If you want to enable this action, you should uncomment the lines in the pre push file.

SWC (Speedy Web Compiler) is an extensible Rust-based platform that can be used for both compilation and bundling. Using SWC with Nest CLI is a great and simple way to significantly speed up your development process.

SWC + Jest error resolution

After applying SWC, the following error was displayed in jest using an in-memory database (pg-mem):

    QueryFailedError: ERROR: function obj_description(regclass,text) does not exist
    HINT: ๐Ÿ”จ Please note that pg-mem implements very few native functions.

                ๐Ÿ‘‰ You can specify the functions you would like to use via "db.public.registerFunction(...)"

    ๐Ÿœ This seems to be an execution error, which means that your request syntax seems okay,
        but the resulting statement cannot be executed โ†’ Probably not a pg-mem error.

    *๏ธโƒฃ Failed SQL statement: SELECT "table_schema", "table_name", obj_description(('"' || "table_schema" || '"."' || "table_name" || '"')::regclass, 'pg_class') AS table_comment FROM "information_schema"."tables" WHERE ("table_schema" = 'public' AND "table_name" = 'user');

    ๐Ÿ‘‰ You can file an issue at https://github.com/oguimbal/pg-mem along with a way to reproduce this error (if you can), and  the stacktrace:

pg-mem is a library designed to emulate PostgreSQL, however, it does not support all features, which is why the above error occurred.

This error can be resolved by implementing or overriding existing functions. Below is the function implementation for the resolution. Related issues can be checked here.

db.public.registerFunction({
  name: 'obj_description',
  args: [DataType.text, DataType.text],
  returns: DataType.text,
  implementation: () => 'test',
});

Todo

  • TDD

    • Unit Test (Use mock)
    • Integration Test (Use in-memory DB)
    • End To End Test (Use docker)
  • Add Many OAUths (Both of front and back end)

    • Kakao
    • Google
    • Apple
    • Naver
  • CI

    • Github actions
    • husky
  • GraphQL Upload

  • Healthcheck

  • Divide usefactory

  • SWC Compiler

  • Refresh Token

  • Redis

  • ElasticSearch

  • Caching

  • Graphql Subscription

  • Remove lodash

  • CASL

nestjs-graphql-typeorm-postgresql's People

Contributors

ho-s avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

nestjs-graphql-typeorm-postgresql's Issues

Same class used for Entity and DTO, but it has some custom properties exposed to API but not stored in DB.

Hello friend. Good work on this repo. I am facing an issue and would like to know your thoughts.

Let's say I have below class:

@ObjectType()
@Entity()
export class User {
  @Field(() => ID)
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Field(() => String)
  @Column()
  username: string;

  @Column()
  yearOfBirth: number;

  @Field(() => Number)
  age: number;
}

When I create new users, I store their year of birth in database, but clients can query user's age directly (which I calculate it in graphql resolver before returning the response to the client). However this doesn't work with your setup since your code iterates through every field and transforms it into Select query before executing it into the DB.

When I run below query:
query User($input: GetOneInput!) {
user(input: $input) {
id
username
age
}
}

I get below error:
"message": "Property "age" was not found in "User". Make sure your query is correct."

How would you approach this?

Thank you in advance.

GetOne has query as undefined

Hello - was trying this and while make a call to the qql endpoint for getoneuser and getme -> the getone method fails as query is undefined. I have added jwt token in header. It calls validate method but the query is undefined.

query GetMe {
getMe {
nickname
id
username
}
}

{
"errors": [
{
"message": "Error",
"path": [
"getMe"
],
"locations": [
{
"line": 2,
"column": 3
}
],
"extensions": {
"__orginal": {
"code": "UNAUTHENTICATED",
"stacktrace": [
"UnauthorizedException: Error",
" at JwtStrategy.validate (C:\D-drive\PropertyGuru\exp\NestJS-GraphQL-TypeORM-PostgresQL-main\src\auth\strategies\jwt.strategy.ts:27:13)",
" at processTicksAndRejections (node:internal/process/task_queues:96:5)",
" at JwtStrategy.callback [as _verify] (C:\D-drive\PropertyGuru\exp\NestJS-GraphQL-TypeORM-PostgresQL-main\node_modules\@nestjs\passport\dist\passport\passport.strategy.js:11:44)"
],
"originalError": {
"message": "Error",
"error": "Body must be a string. Received: undefined.",
"statusCode": 401
}
},
"code": "UNAUTHENTICATED",
"message": "Error"
}
}
],
"data": null
}

Error with Body must be a string. Received: undefined.

Hello @Ho-s
Thanks for this awesome boilerplate.
Unfortunately i've got some error > Error with Body must be a string. Received: undefined.

This is what i've tried.

  1. Clone this repo and install package with yarn install (node > 14)

  2. Run postgres with docker docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=1q2w3e4r -d postgres

image
  1. Generate jwt token and save on .development.env
.development.env
DB_HOST = localhost
DB_PORT = 5432
DB_USER = postgres
DB_PASSWORD = 1q2w3e4r
DB_NAME = postgres
PORT = 3000

JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----"

JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3F65WsAW2eI4qnrzEu/K
oYq1Bn/zJRpEsBkQIz0g/sKhNJ0D4pLfEIF6ZJGFFI9NJNoSoIrg7SfjhvHNhP6A
Fsa3QNjkMrOCC2d/6xW8oFcsS++1MqJEsn5TJSm98rNvXpPT/DGFdUxE7cmoCL0k
lwzt+xk7eBRDUWhE3Z+1TBmVYEkO9oFVdCyXOwsEDlzsbuccxfrgwmf2WZne4Ecn
oy5i1nJci1TPDRgqOA9IrGncIdHIj80Fope3x3W42nKY8SBoI3HqSTQ08j7Ci7gq
xeBC5x6b847iWjqN7G978oPiD9PG7dEd8IWkrfjt1ru2NDcPO8reRHt1EUEyrMg2
epVi5uGpKoC4QouRVI+EeaA+zV9Q/wwwMOyNlaJJxZXWGPaDJSMID1uTqAdyjUL5
R0vk0TiBIGFxavxtBmJpdZhS9APs1vPPRDq/O6A+ZqnlwafaEJspQSn58xkiHWS3
ivOANlQuUMtnxoa+qeV8TxBGWNaKlisdRl2vIChMp5zLdArp/N4qm41Nsvfie7Nl
q5MFC/hS8JZuRewj1W1KR2D8anLfKD93Uf5Zml9FLUnwg7r67XNdwYwDb7BSLUdZ
hJCavbqbnEPnnXtPoxJ/IfCiIbJ2U6o7Rvbai0gnLx1kGkBxK67+kyhYl4/QsAE7
FF4BGAetAZ5OuqhefWlYcLsCAwEAAQ==
-----END PUBLIC KEY-----"

  1. Run yarn dev

  2. make mutation with playground

image
mutation Mutation($input: SignUpInput!) {
  signUp(input: $input) {
    jwt
    user {
      username
      createdAt
      updatedAt
    }
  }
}
{
  "input": {
    "nickname": "123",
    "password": "123",
    "username": "123"
  }
}
  1. And i got this error
    Error with Body must be a string. Received: undefined.

  2. In auth.service.ts - signUp i tired to debug with console.log

 async signUp(input: SignUpInput): Promise<JwtWithUser> {
    console.log(input);
    const doesExistId = await this.userService.getOne({
      where: { username: input.username },
    });
    if (doesExistId) {
      throw new BadRequestException('Username already exists');
    }
    console.log('hi');
    const user = await this.userService.create(input);

    return this.signIn(user);
  }
  1. And i got this output
SignUpInput { nickname: '123', password: '123', username: '123' }
[Nest] 67970  - 11/14/2023, 4:56:13 PM   ERROR [ExceptionsHandler] Body must be a string. Received: undefined.
  1. in user.service.ts tried to console.log query and it says undefined
getOne(qs: OneRepoQuery<User>, query?: string) {
  console.log(qs, query);
  return this.userRepository.getOne(qs, query);
}
{ where: { username: '123' } } undefined
  1. tried to remove check user exist, then mutation works well
  async signUp(input: SignUpInput): Promise<JwtWithUser> {
    console.log(input);
    // const doesExistId = await this.userService.getOne({
    //   where: { username: input.username },
    // });
    // console.log('hi1');
    // if (doesExistId) {
    //   throw new BadRequestException('Username already exists');
    // }

    const user = await this.userService.create(input);

    return this.signIn(user);
  }
image

the error might be belongs to custom CRUD
Can i get any helps for this ? ๐Ÿง

Querying relations through GraphQL

I noticed that GetManyInput does not have a relations key, and it doesn't seem like there is an automatic way (or manual way) to specify which relations should load, so they are being returned as null when requested through queries. Is this something that is to be implemented?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.