In order of level of abstraction, where 1 is the low level and 3 the high level.
- Native driver: allows to write SQL queries directly.
- Query Builder: allows to write SQL queries with a JavaScript abstraction.
- ORM - Object-Relational Mapping: creates a relation between the application's models and the database tables.
It's recommended to use the ORM.
- creates an isolated environment (container).
- containers expose some ports for communications.
Installation instructions: https://docs.docker.com/engine/install/ubuntu/
- Image: is a service offered via docker
- Container: is an instance of an image.
- Docker Registry (Docker Hub): is a centralized service where docker images are stored.
- Dockerfile - Image Recipe: a script that starts a container.
Before installing a postgresql container, check if you already have one available.
Basic docker commands:
docker ps # list active containers
docker ps -a # list available containers in your machine
docker start containerName # starts containerName
docker stop containerName # stops containerName
docker logs containerName # show containerName logs
docker run # runs a process in a new container
# by the way, here's a command to check if a specific port is open:
sudo lsof -i :portNumber
Installing a PostgreSQL container:
docker run --name gostack_postgresql -e POSTGRES_PASSWORD=docker -p 5432:5432 -d postgres
# --name=""
# Assign a name to the container
# -e, --env=[]
# Set environment variables
# -p, --publish ip:[hostPort]:containerPort | [hostPort:]containerPort
# Publish a container's port, or range of ports, to the host.
# -d, --detach=true|false
# Detached mode: run the container in the background and print the new container ID. The default is false.
Note: in the option -p
, the first number is the port of the "real" machine, and the number after :
is the container's port.
- DBeaver: https://dbeaver.io/
- Postbird: https://www.electronjs.org/apps/postbird
- Beekeeper Studio: https://www.beekeeperstudio.io/
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/banco-de-dados/lesson/configurando-type-orm
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/83179555b7caad2b6ff788aff0e64ebe66b24f08#diff-2efc37c87c194d03fc0dadbef51f8814
TypeORM website: https://typeorm.io/
yarn add typeorm pg # pg for postgresql driver
Create a database named gostack_gobarber
(using dbeaver or something).
ormconfig.json
:
{
"type": "postgres",
"host": "localhost", // host of your database
"port": 5432,
"username": "postgres",
"password": "docker",
"database": "gostack_gobarber"
}
src/database/index.ts
:
import { createConnection } from 'typeorm';
createConnection();
src/server.ts
:
import express from 'express';
import routes from './routes';
import './database'; // <-- this is enough to connect to the DB
const app = express();
app.use(express.json());
app.use(routes); // <-- real logic happens here
app.listen(3333, () => console.log('server started'));
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/banco-de-dados/lesson/criando-tabela-de-agendamentos
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/3893bd76a55a451c1e91a9677260530eb4982229#diff-2efc37c87c194d03fc0dadbef51f8814
- add
migrations[]
andcli.migrationsDir
toormconfig.json
. - add
scripts.typeorm
topackage.json
.
# create a migration
yarn typeorm migration:create -n CreateAppointments
src/database/migrations/*-CreateAppointments.ts
.
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
export default class CreateAppointments
implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'appointments',
columns: [
{
name: 'id',
type: 'varchar', // in a later lesson it's changed to 'uuid'
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'provider',
type: 'varchar',
},
{
name: 'date',
type: 'timestamp with time zone',
},
],
}),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('appointments');
}
}
# run the migrations
yarn typeorm migration:run
IMPORTANT: if while running the migration you receive this error:
QueryFailedError: function uuid_generate_v4() does not exist
Then run this command inside PostgreSQL, in the gostack_gobarber
database:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
# create a migration
yarn typeorm migration:create -n CreateAppointments
# run the migrations
yarn typeorm migration:run
# revert the migrations
yarn typeorm migration:revert
# show the executed migrations
yarn typeorm migration:show
Why migrations? - To assure the database is equal to every developer.
IMPORTANT!!!: You can only change a migration BEFORE commiting it to your version control system (git). Otherwise, create a new migration changing the table(s) the you way you need.
If wanted, disable the eslint's class-method-use-this
rule:
.eslintrc.json
:
{
// ...
"rules": {
"class-methods-use-this": "off",
// ...
}
// ...
}
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/banco-de-dados/lesson/criando-model-de-agendamento
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/11ed5c8e6678c781f851a1d11af96cf6d3714d7a#diff-2efc37c87c194d03fc0dadbef51f8814
Using the Entity
from the typeorm package.
In tsconfig.json
, enable experimentalDecorators
e emitDecoratorMetadata
, and disable strictPropertyInitialization
.
When using Entity
there's no need to use the model's constructor.
src/models/Appointments.ts
:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity('appointments')
class Appointment {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
provider: string;
@Column('timestamp with time zone')
date: Date;
}
export default Appointment;
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/banco-de-dados/lesson/repositorio-do-type-orm
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/66833fbaca66266169af584d49253aab87ce7124#diff-2efc37c87c194d03fc0dadbef51f8814
In order to use decorators, TypeORM requires you to use the following package:
yarn add reflect-metadata
And in the src/server.ts
:
import 'reflect-metadata';
Also add entities[]
to ormconfig.json
.
Most of the repositories methods (such as constructor()
, all()
, create()
) are already provided by the TypeORM. We just need to
extend the Repository
class.
src/repositories/AppointmentsRepository.ts
:
import { EntityRepository, Repository } from 'typeorm';
import Appointment from '../models/Appointment';
@EntityRepository(Appointment)
class AppointmentsRepository extends Repository<Appointment> {
public async findByDate(date: Date): Promise<Appointment | null> {
const findAppointment = await this.findOne({
where: { date }, // `SELECT * FROM appointments WHERE date = '${date}';`
}); // test the getSql(), looks useful as a learning tool
return findAppointment || null;
}
}
export default AppointmentsRepository;
After changing the AppointmentsRepository
, it's needed to adapt the Service and the Route accordingly.
- install docker: https://docs.docker.com/engine/install/ubuntu/
- install a postgres container
docker run --name gostack_postgresql -e POSTGRES_PASSWORD=docker -p 5432:5432 -d postgres
# Basic docker commands:
docker ps # list active containers
docker ps -a # list available containers in your machine
docker start containerName # starts containerName
docker stop containerName # stops containerName
docker logs containerName # show containerName logs
docker run # runs a process in a new container
-
install a DB client (recommended: DBeaver)
-
create the
gostack_gobarber
database in the postgresql (via DB client). -
install typeorm and other dependencies
yarn add typeorm pg reflect-metadata
- add typeorm to the
packages.json
:
"scripts": {
"typeorm": "ts-node-dev ./node_modules/typeorm/cli.js"
}
-
ormconfig.json
:
{
"type": "postgres",
"host": "localhost", // host of your database
"port": 5432,
"username": "postgres",
"password": "docker",
"database": "gostack_gobarber",
"entities": [
"./src/models/*.ts"
],
"migrations": [
"./src/database/migrations/*.ts"
],
"cli": {
"migrationsDir": "./src/database/migrations"
}
}
-
src/database/index.ts
for connection to the database:
import { createConnection } from 'typeorm';
createConnection();
- in
src/server.ts
:
import 'reflect-metadata';
// ...
import './database';
// ...
- create the
CreateAppointments
migration:
yarn typeorm migration:create -n CreateAppointments
- edit the
src/database/migrations/*CreateAppointments.ts
migration like this and then run it:
yarn typeorm migration:run
-
if needed, add the rule
"class-methods-use-this": "off"
to.eslintrc.json
. -
in
tsconfig.json
be sure to haveexperimentalDecorators
andemitDecoratorMetadata
as true, andstrictPropertyInitialization
as false. -
create/edit the model
src/models/Appointments.ts
(usingEntity
):
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity('appointments')
class Appointment {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
provider: string;
@Column('timestamp with time zone')
date: Date;
}
export default Appointment;
- create/edit the repository
src/repositories/AppointmentsRepository.ts
:
import { EntityRepository, Repository } from 'typeorm';
import Appointment from '../models/Appointment';
@EntityRepository(Appointment)
class AppointmentsRepository extends Repository<Appointment> {
public async findByDate(date: Date): Promise<Appointment | null> {
const findAppointment = await this.findOne({
where: { date }, // `SELECT * FROM appointments WHERE date = '${date}';`
});
return findAppointment || null;
}
}
export default AppointmentsRepository;
-
Adapt
src/services/CreateAppointmentService.ts
using the repository. -
Adapt
src/routes/appointments.routes.ts
using the repository. -
Test the application with insomnia.
- Create some appointments.
- List all appointments.
- Turn off the server and then launch it again, and check if the previously created appointments persisted.
https://github.com/meleu/gobarber/tree/59fc063f4bac19803731253851cbaeb47a895918