- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/cadastro-de-usuarios/lesson/model-e-migration-de-usuarios
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/0d9d4f689daddba02deaa44c3660031ea3f25f44#diff-2efc37c87c194d03fc0dadbef51f8814
yarn typeorm migration:create -n CreateUsers
- Columns in a minimalistic users table:
- id
- name
- password
- created_at
- updated_at
Edit the src/database/migrations/*CreateUsers.ts
:
import { MigrationInterface, QueryRunner, Table } from 'typeorm';
export default class CreateUsers1601474273498 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'users',
columns: [
{
name: 'id',
type: 'varchar', // later it'll be changed to uuid
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'name',
type: 'varchar',
},
{
name: 'email',
type: 'varchar',
isUnique: true,
},
{
name: 'password',
type: 'varchar',
},
{
name: 'created_at',
type: 'timestamp',
default: 'now()',
},
{
name: 'updated_at',
type: 'timestamp',
default: 'now()',
},
],
}),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable('users');
}
}
And then:
yarn typeorm migration:run
Create the src/models/User.ts
:
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
@Entity('users')
class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column()
email: string;
@Column()
password: string;
@CreateDateColumn()
created_at: Date;
@UpdatedDateColumn()
updated_at: Date;
}
Note: it's quite common to have the fields created_at
and updated_at
in every database table.
Add the fields created_at
and updated_at
to the appointments
migration/model. You'll need to:
- Revert the migration(s)
- Edit the appointment's migration
- Run the migrations again
- Edit the appointment's model accordingly
- (check how the database is filled)
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/cadastro-de-usuarios/lesson/relacionamento-nos-models
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/b93e78b59f643ff8d51af16dbe8c6f3d4009bb85#diff-2efc37c87c194d03fc0dadbef51f8814
Note: during the video, around 7min, it's possible to see how TypeScript+VSCode can make coding faster.
Creating a relationship between the appointments and the users table (as the provider in fact is an user).
-
In the
src/models/Appointments.ts
, replace the columnprovider
withprovider_id
. -
In the
*CreateUsers
and*CreateAppointments
migrations, change theid
column's type fromvarchar
touuid
. -
Revert the migrations and then run them again.
-
Create a migration to change the appointments table:
yarn typeorm migration:create -n AppointmentsProviderIsAnUser
4.1. drop the column provider
4.2. add the column provider_id
4.3. create a foreign key referencing users.id
Note 1: in the appointments table, the provider_id
is nullable, in order to allow removing provider's info from the database without messing with client's appointments log. Also onDelete: 'SET NULL'
to achieve this goal.
Note 2: in a migration that alters a table, the down()
method MUST be changed in the specific reverse order of the sequence of changes being done in the up()
method.
-
Run the migrations
-
INTERESTING!: DBeaver has a feature to show the ER Diagrams! Seems to be quite handy!
- IMPORTANT: the Appointments model can have a property, which is NOT a column in the appointments table, to provide an easy access to the User bound to the
provider_id
:
// ...
import User from './User';
// ...
class Appointment {
// ...
@ManyToOne(() => User)
@JoinColumn({ name: 'provider_id' })
provider: User;
// ...
}
- Test with insomnia (at least to read some error messages).
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/cadastro-de-usuarios/lesson/criacao-de-registros
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/1df7df48c4949a1751006b6a797f8887da0eeee2#diff-2efc37c87c194d03fc0dadbef51f8814
- create
src/routes/users.routes.ts
. - import the users route in the
src/routes/index.ts
. - create the
src/services/CreateUserService
handling the business rules.
- Note: There's no need to write a repository code if the model has only the default CRUD operations. Just use the
getRepository()
method (fromtypeorm
) using the model as the argument and you'll have access to the repositoriy's methods.
- adapt the
appointments.routes.ts
and theCreateAppointmentService.ts
to useprovider_id
rather thanprovider
.
- if needed, disable eslint camelCase.
- test with insomnia.
- video: https://app.rocketseat.com.br/node/iniciando-back-end-do-app/group/autenticacao/lesson/conceitos-de-jwt-2
- commit: https://github.com/rocketseat-education/bootcamp-gostack-modulos/commit/b802cea5afa87d2999a1681238401387f10bba42#diff-2efc37c87c194d03fc0dadbef51f8814
- goals:
- encrypt the password before persisting it.
- do not give the password in the response.
yarn add bcryptjs
yarn add -D @types/bcryptjs
src/services/CreateUserService.ts
:
import { hash } from 'bcryptjs'
// ...
public async execute() { // ...
// ...
const hashedPassword = await hash(password, 8); // magic number?!
const user = usersRepository.create({
name,
email,
password: hashedPassword,
});
// ...
}
src/routes/users.routes.ts
:
// ...
// destructuring but ignoring the hashed password
const { id, name, email, created_at, updated_at } = await createUser.execute({
name,
email,
password
});
const user = {
id,
name,
email,
created_at,
updated_at
}
return response.json(user);
// ...
Files that need to be created/coded in order to add Users to the project.
- Users migration.
- Columns in a minimalistic users table:
- id
- name
- password
- created_at
- updated_at
- Columns in a minimalistic users table:
- User's model.
- UserRepository (in this case it won't be necessary, as we're using only methods already available in the TypeORM's repository implementation).
-
CreateUserService
to create a new user in the repository.- some business rules:
- email must be unique
- email must a valid email address
- password cannot be empty
- password must be hashed (
hash()
method frombcryptjs
)
- some business rules:
- Route to create a new user
- Note: the response must not answer with the password.
- Create the relation between appointments and users (a provider is a user)
- In the Appointments model, replace
provider
withprovider_id
. - Create an
AppointmentsProviderIsAnUser
migration.- drop column provider
- add column provider_id
- create a foreign key referencing provider_id to users.id
- the down() method must undo that 👆 in the reverse order.
- Again in the Appointments' model, add a property provider to allow an easy access to the provider's info (
@ManyToOne
,@JoinColumn
, etc.).
- In the Appointments model, replace
I didn't really understood those decorators used in the models. I need to research more about that later.
https://github.com/meleu/gobarber/tree/184ad075b0c624f7020da01da96286690549a39a