A progressive Node.js framework for building efficient and scalable server-side applications.
The origin of this project is: https://github.com/nestjs/schematics
The application files have been adapted according to the descriptions in my blog.
Additional generators:
database
ordb
: add MikroORM based database connectiontype
defines the default database type, e.g.sqlite
openapi
orapi
: Generate a server (Controller + DTO) from a OpenAPI 3 JSON file- first commandline parameter: file name of the OpenAPI JSON
- second command line parameter: name of the controller (optional, otherwise, contoler name is taken from first path element)
kafka
: add kafka connection (not started yet)
Also Generates:
Dockerfile
docker-compose.yaml
npm i -g @mwaeckerlin/schematics
nest new -c @mwaeckerlin/schematics [options] [name]
nest new -c @mwaeckerlin/schematics -p npm -l ts test
cd test
npm install
npm run start:debug
Or:
docker-compose up --build
Create a database, run initial migration (if you don't use SQLitem then you need to run a database with docker-compose up -d db
for the initial migration):
nest new -c @mwaeckerlin/schematics -p npm test
cd test
nest g -c @mwaeckerlin/schematics db
nest g -c @mwaeckerlin/schematics res user
npm install
npm run build
docker-compose up -d db
npm run migration:initial
docker-compose up
If you get:
code: 'ER_ACCESS_DENIED_ERROR',
errno: 1045,
sqlState: '28000',
sqlMessage: "Access denied for user 'user'@'172.24.0.1' (using password: YES)",
Then you probably still have an old docker volume created with another password:
$ docker volume ls
DRIVER VOLUME NAME
local test_db-volume
Remove it:
docker-compose rm -vfs
docker volume rm test_db-volume
Or remove everything:
docker system prune --all --volumes
Let's implement the MikroORM example from my Blog: A databse with Author, Publisher and Book, where the Book refers to any number of Authors and Publishers, while there is no reference from Author or Publisher to the Book (unidirectional).
nest new -c @mwaeckerlin/schematics -p npm test
cd test
nest g -c @mwaeckerlin/schematics db
nest g -c @mwaeckerlin/schematics res author
nest g -c @mwaeckerlin/schematics res punlisher
nest g -c @mwaeckerlin/schematics res book
First we define the entities (the database schema).
Please note that comments are automatically added to the return value description in the generated API documentation.
Just add the properties:
import { Entity, Property } from '@mikro-orm/core'
import { Base } from '../../base/entities/base.entity'
import { CreateAuthorDto } from '../dto/create-author.dto'
@Entity()
export class Author extends Base {
constructor(createAuthorDto: CreateAuthorDto) {
super()
Object.assign(this, createAuthorDto)
}
/* author's first name(s) */
@Property()
first_names?: string[]
/* author's family name(s) */
@Property()
last_names!: string[]
/* date of birth of the author */
@Property()
born?: Date
/* date of death of the author, if applicable */
@Property()
died?: Date
}
Just add the properties:
import { Entity, Property } from '@mikro-orm/core'
import { Base } from '../../base/entities/base.entity'
import { CreatePublisherDto } from '../dto/create-publisher.dto'
@Entity()
export class Publisher extends Base {
constructor(createPublisherDto: CreatePublisherDto) {
super()
Object.assign(this, createPublisherDto)
}
/* name(s) of the publisher */
@Property()
publisher_names!: string[]
/* full address of the publisher, may contain several lines */
@Property()
publisher_address_lines?: string[]
}
The book is a little bit more complex, since it refers to Author and Publisher:
import { Collection, Entity, ManyToMany, Property } from '@mikro-orm/core'
import { Author } from '../author/entities/author.entity'
import { Publisher } from '../publisher/entities/publisher.entity'
import { Base } from '../../base/entities/base.entity'
import { CreateBookDto } from '../dto/create-book.dto'
@Entity()
export class Book extends Base {
constructor(createBookDto: CreateBookDto, authors?: Author[], publishers?: Publisher[]) {
super()
Object.assign(this, {...createBookDto, authors, publishers})
}
/* title(s) of the book */
@Property()
titles!: string[]
/* full structure of the author(s) of the book */
@ManyToMany()
authors = new Collection<Author>(this)
/* full structure of the publisher(s) of the book */
@ManyToMany()
publishers = new Collection<Publisher>(this)
/* ISBN is available */
@Property()
isbn?: string
}
The update DTOs are generic, but in the creation DTOs you need to set the values that will be passed thriough the REST API.
Please note that comments are automatically added to the interface description.
export class CreateAuthorDto {
/* author's first name(s) */
first_names?: string[]
/* author's family name(s) */
last_names!: string[]
/* date of birth of the author */
born?: Date
/* date of death of the author, if applicable */
died?: Date
}```
##### Publisher
```typescript
export class CreatePublisherDto {
/* name(s) of the publisher */
publisher_names?: string[]
/* full address of the publisher, may contain several lines */
publisher_address_lines?: string[]
}
At creation, authors and publishers are referenced as ids.
export class CreateBookDto {
/* title(s) of the book */
titles?: string[]
/* database id(s) of the author(s) of the book */
authors?: string[]
/* database id(s) of the publisher(s) of the book */
publishers?: string[]
/* ISBN is available */
isbn?: string
}
Only the Book service needs changes because the Book needs to refer to Author and Publisher. All controlers and the services of Author and Publisher remain unchanged.
import { Injectable, Logger, NotFoundException } from '@nestjs/common'
import { Book } from './entities/book.entity'
import { CreateBookDto } from './dto/create-book.dto'
import { UpdateBookDto } from './dto/update-book.dto'
import { EntityManager } from '@mikro-orm/core'
import { Publisher } from '../publisher/entities/publisher.entity'
import { Author } from '../author/entities/author.entity'
@Injectable()
export class BookService {
private readonly logger = new Logger(this.constructor.name)
constructor(private readonly em: EntityManager) { }
async create(createBookDto: CreateBookDto): Promise<Book> {
return await this.em.transactional(async (em) => {
const authors = await em.find(Author, { id: { $in: createBookDto.authors ?? [] } })
if ((authors?.length ?? 0) !== (createBookDto?.authors?.length ?? 0)) throw new NotFoundException('author not found')
const publishers = await em.find(Publisher, { id: { $in: createBookDto.publishers ?? [] } })
if ((publishers?.length ?? 0) !== (createBookDto?.publishers?.length ?? 0)) throw new NotFoundException('publisher not found')
const book = new Book(createBookDto, authors, publishers)
await em.persistAndFlush(book)
return book
})
}
async findAll(query: Object = {}): Promise<Book[]> {
return this.em.find(Book, query, { populate: ['authors', 'publishers'] })
}
async findOne(id: string): Promise<Book> {
return this.em.findOneOrFail(Book, id, { populate: ['authors', 'publishers'] })
}
async update(id: string, updateBookDto: UpdateBookDto): Promise<Book> {
return await this.em.transactional(async (em) => {
const authors = await em.find(Author, { id: { $in: updateBookDto.authors ?? [] } })
if ((authors?.length ?? 0) !== (updateBookDto?.authors?.length ?? 0)) throw new NotFoundException('author not found')
const publishers = await em.find(Publisher, { id: { $in: updateBookDto.publishers ?? [] } })
if ((publishers?.length ?? 0) !== (updateBookDto?.publishers?.length ?? 0)) throw new NotFoundException('publisher not found')
const book = await em.findOneOrFail(Book, { id })
Object.assign(book, {
...updateBookDto,
authors: updateBookDto.authors === null ? book.authors : authors,
publishers: updateBookDto.publishers === null ? book.publishers : publishers
})
await em.persistAndFlush(book)
return book
})
}
async remove(id: string): Promise<Book> {
const book = await this.em.findOneOrFail(Book, id)
await this.em.removeAndFlush(book)
return book
}
}
That's it, prepare it and generate the first migration:
npm i
npm run build
npm run migration:initial
Start it:
npm start
Then browse to [http://localhost:4000/api] and play with the API!
The Nest CLI is a command-line interface tool that helps you to initialize, develop, and maintain your Nest applications. It assists in multiple ways, including scaffolding the project, serving it in development mode, and building and bundling the application for production distribution. It embodies best-practice architectural patterns to encourage well-structured apps. Read more here.
$ npm install -g @mwaeckerlin/schematics
To use @mwaeckerlin/schematics
, you need to explicitly refere to it:
$ nest new -c @mwaeckerlin/schematics [options] [name]
Learn more in the official documentation.
- Website - https://nestjs.com
- Twitter - @nestframework
Nest is MIT licensed.