Skip to content

Commit

Permalink
replace mongo with postgresql
Browse files Browse the repository at this point in the history
  • Loading branch information
mariovyord committed Jan 7, 2024
1 parent cd5b0c4 commit 18d0cbd
Show file tree
Hide file tree
Showing 31 changed files with 1,057 additions and 557 deletions.
759 changes: 704 additions & 55 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@
"helmet": "^7.1.0",
"joi": "^17.11.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.2",
"mongoose-beautiful-unique-validation": "^7.1.1",
"morgan": "^1.10.0",
"pg": "^8.11.3",
"reflect-metadata": "^0.2.1",
"swagger-ui-express": "^5.0.0",
"typeorm": "^0.3.19",
"winston": "^3.11.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express from "express";
import cookieParser from "cookie-parser";
import cors from "cors";
import router from "./root-router";
import router from "./router";
import path from "path";
import handleErrors from "./middleware/error-middleware";
import loggerMiddleware from "./middleware/logger-middleware";
Expand Down
29 changes: 16 additions & 13 deletions src/config/db.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import mongoose from "mongoose";
import { DataSource } from "typeorm";
import "reflect-metadata";
import { Article } from "../features/article/article-entity";
import { Comment } from "../features/comment/comment-entity";
import { User } from "../features/user/user-entity";

export default async function db(dbConnectionString: string) {
try {
await mongoose.connect(dbConnectionString),
{
useNewUrlParser: true,
useUnifiedTopology: true,
};
} catch (err) {
console.error("Error connecting to database");
process.exit(1);
}
}
export const AppDataSource = new DataSource({
type: "postgres",
host: "localhost",
port: 6543,
password: "123123",
username: "postgres",
database: "express_app",
synchronize: true,
logging: true,
entities: [Article, Comment, User],
});
24 changes: 24 additions & 0 deletions src/features/article/article-entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm";
import { User } from "../user/user-entity";

@Entity()
export class Article {
@PrimaryGeneratedColumn("uuid")
id: string;

@Column({ type: "varchar", length: 300 })
title: string;

@Column({ type: "varchar", length: 2000 })
content: string;

@OneToOne(() => User)
@JoinColumn()
owner: string;

@Column({ type: "date" })
updated_at: Date;

@Column({ type: "date" })
created_at: Date;
}
2 changes: 1 addition & 1 deletion src/features/article/article-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as articleService from "./article-service";
import { NextFunction, Request, Response } from "express";
import { BadRequestError, InternalServerError, NotFoundError } from "../../utils/app-error";
import { HttpStatusCode } from "../../utils/http-status-code";
import { IFullQuery } from "../../utils/parse-query";
import { IFullQuery } from "../../utils/build-query";

export function getAllArticles() {
return async (req: Request<{}, {}, {}, IFullQuery>, res: Response<IJsonResponse>, next: NextFunction) => {
Expand Down
51 changes: 0 additions & 51 deletions src/features/article/article-queries.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/features/article/article-repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { AppDataSource } from "../../config/db";
import { Article } from "./article-entity";

export const articleRepository = () => AppDataSource.getRepository(Article);
30 changes: 0 additions & 30 deletions src/features/article/article-schema.ts

This file was deleted.

58 changes: 32 additions & 26 deletions src/features/article/article-service.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import { NotFoundError, UnauthorizedError } from "../../utils/app-error";
import { IFullQuery, parseQueryToMongoParams } from "../../utils/parse-query";
import * as articleQueries from "./article-queries";
import { ArticleDto, ICreateArticleData, IPatchArticleData } from "./article-types";
import { IFullQuery, buildQuery } from "../../utils/build-query";
import { Article } from "./article-entity";
import { articleRepository } from "./article-repository";
import { ArticleDto, ICreateArticleData, IPutArticleData } from "./article-types";

export async function getAll(query: IFullQuery): Promise<ArticleDto[] | number> {
const parsedQuery = parseQueryToMongoParams(query);
export async function getAll(query: IFullQuery): Promise<ArticleDto[]> {
const articles = await buildQuery(articleRepository(), query);

if (parsedQuery.count) {
return articleQueries.countDocumentsByQuery(parsedQuery);
if (!articles) {
return [];
}

const articles = await articleQueries.findArticlesByQuery(parsedQuery);

return articles.map((x) => new ArticleDto(x));
}

export async function getOne(id: string, query: any): Promise<ArticleDto | null> {
let populate = "";
let limitPopulate = "";
export async function getOne(id: string, query: any): Promise<ArticleDto> {
let queryBuilder = articleRepository().createQueryBuilder("entity");

if (query && query.populate) {
populate += query.populate;
queryBuilder = queryBuilder.where("entity.id = :id", { id });

if (query.populate.includes("owner")) {
limitPopulate += "username firstName lastName";
}
if (query && query.populate) {
queryBuilder = queryBuilder.leftJoinAndSelect(`entity.${query.populate}`, query.populate);
}

const article = await articleQueries.findArticleById(id, { populate, limitPopulate });
const article = await queryBuilder.getOne();

if (!article) {
throw new NotFoundError();
Expand All @@ -36,15 +32,23 @@ export async function getOne(id: string, query: any): Promise<ArticleDto | null>
return new ArticleDto(article);
}

export async function create(data: ICreateArticleData): Promise<ArticleDto> {
const article = await articleQueries.createArticle(data);
export async function create(data: ICreateArticleData & { owner: string }): Promise<ArticleDto> {
const article = new Article();
article.content = data.content;
article.title = data.title;
article.owner = data.owner;
article.created_at = new Date();
article.updated_at = new Date();

await articleRepository().save(article);

return new ArticleDto(article);
}

const ALLOWED_UPDATE_FIELDS = ["title", "content"];

export async function update(id: string, userId: string, data: IPatchArticleData): Promise<ArticleDto> {
const article = await articleQueries.findArticleById(id);
export async function update(id: string, userId: string, data: IPutArticleData): Promise<ArticleDto> {
const article = await articleRepository().findOneBy({ id });

if (article === null) throw new NotFoundError();
if (article.owner.toString() !== userId) throw new UnauthorizedError();
Expand All @@ -55,16 +59,18 @@ export async function update(id: string, userId: string, data: IPatchArticleData
}
}

await articleQueries.saveUpdatedArticle(article);
article.updated_at = new Date();

await articleRepository().save(article);

return new ArticleDto(article);
}

export async function remove(id: string, userId: string): Promise<void> {
const article = await articleQueries.findArticleById(id);
const article = await articleRepository().findOneBy({ id });

if (!article) throw new NotFoundError();
if (article.owner.toString() !== userId) throw new UnauthorizedError();
if (article.owner !== userId) throw new UnauthorizedError();

await articleQueries.deleteArticleById(id);
await articleRepository().remove(article);
}
32 changes: 14 additions & 18 deletions src/features/article/article-types.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { ObjectId } from "mongodb";
import { IUser, UserDto } from "../user/user-types";
import { isValidObjectId } from "mongoose";
import { Article } from "./article-entity";

export interface IArticle {
_id: ObjectId;
export type ICreateArticleData = {
title: string;
content: string;
owner: ObjectId | IUser;
createdAt: Date;
updatedAt: Date;
estimatedDocumentCount: () => number;
count: () => number;
}
};

export type ICreateArticleData = Pick<IArticle, "title" | "content">;

export type IPatchArticleData = Pick<IArticle, "title" | "content">;
export type IPutArticleData = {
title: string;
content: string;
};

/**
* Represents a Public Article with limited information.
Expand All @@ -28,17 +24,17 @@ export class ArticleDto {
createdAt: Date;
updatedAt: Date;

constructor(article: IArticle) {
this.id = article._id.toString();
constructor(article: Article) {
this.id = article.id;
this.title = article.title;
this.content = article.content;
this.createdAt = article.createdAt;
this.updatedAt = article.updatedAt;
this.createdAt = article.created_at;
this.updatedAt = article.updated_at;

if (isValidObjectId(article.owner)) {
this.owner = (article.owner as ObjectId).toString();
if (typeof article.owner === "string") {
this.owner = article.owner;
} else {
this.owner = new UserDto(article.owner as IUser);
this.owner = new UserDto(article.owner);
}
}
}
4 changes: 2 additions & 2 deletions src/features/article/article-validators.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Joi from "joi";
import { ICreateArticleData, IPatchArticleData } from "./article-types";
import { ICreateArticleData, IPutArticleData } from "./article-types";

export const articleCreateSchema = Joi.object<ICreateArticleData>().keys({
title: Joi.string().required().min(3).max(100),
content: Joi.string().required().min(3).max(10000),
});

export const articlePatchSchema = Joi.object<IPatchArticleData>().keys({
export const articlePatchSchema = Joi.object<IPutArticleData>().keys({
title: Joi.string().optional().min(3).max(100),
content: Joi.string().optional().min(3).max(10000),
});
29 changes: 29 additions & 0 deletions src/features/comment/comment-entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm";
import { User } from "../user/user-entity";

@Entity()
export class Comment {
@PrimaryGeneratedColumn("uuid")
id: string;

@Column({ type: "varchar", length: 2000 })
content: string;

@OneToOne(() => User)
@JoinColumn()
owner: string;

@OneToOne(() => Comment, { nullable: true })
@JoinColumn()
parent: string;

@OneToOne(() => User)
@JoinColumn()
article: string;

@Column({ type: "date" })
updated_at: Date;

@Column({ type: "date" })
created_at: Date;
}
2 changes: 1 addition & 1 deletion src/features/comment/comment-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as commentService from "./comment-service";
import { NextFunction, Request, Response } from "express";
import { BadRequestError, InternalServerError, NotFoundError } from "../../utils/app-error";
import { HttpStatusCode } from "../../utils/http-status-code";
import { IFullQuery } from "../../utils/parse-query";
import { IFullQuery } from "../../utils/build-query";

export function getAllComments() {
return async (req: Request<{}, {}, {}, IFullQuery>, res: Response<IJsonResponse>, next: NextFunction) => {
Expand Down
Loading

0 comments on commit 18d0cbd

Please sign in to comment.