mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-24 06:28:07 +01:00
Database changes. Added Snippet model and controller
This commit is contained in:
13
src/controllers/snippets.ts
Normal file
13
src/controllers/snippets.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { asyncWrapper } from '../middleware';
|
||||
import { SnippetModel } from '../models';
|
||||
|
||||
export const createSnippet = asyncWrapper(
|
||||
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
const snippet = await SnippetModel.create(req.body);
|
||||
|
||||
res.status(201).json({
|
||||
data: snippet
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1,9 +1,9 @@
|
||||
require('ts-node/register');
|
||||
import path from 'path';
|
||||
import { Sequelize } from 'sequelize';
|
||||
import { Umzug, SequelizeStorage } from 'umzug';
|
||||
import Umzug from 'umzug';
|
||||
import { Logger } from '../utils';
|
||||
|
||||
const logger = new Logger();
|
||||
const logger = new Logger('db');
|
||||
|
||||
// DB config
|
||||
export const sequelize = new Sequelize({
|
||||
@@ -14,15 +14,21 @@ export const sequelize = new Sequelize({
|
||||
|
||||
// Migrations config
|
||||
const umzug = new Umzug({
|
||||
migrations: { glob: '**/migrations/*.ts' },
|
||||
context: sequelize.getQueryInterface(),
|
||||
storage: new SequelizeStorage({ sequelize }),
|
||||
logger: undefined
|
||||
migrations: {
|
||||
path: path.join(__dirname, './migrations'),
|
||||
params: [sequelize.getQueryInterface()],
|
||||
pattern: /^\d+[\w-]+\.(js|ts)$/
|
||||
},
|
||||
storage: 'sequelize',
|
||||
storageOptions: {
|
||||
sequelize
|
||||
},
|
||||
logging: false
|
||||
});
|
||||
|
||||
export type Migration = typeof umzug._types.migration;
|
||||
|
||||
export const connectDB = async () => {
|
||||
const isDev = process.env.NODE_ENV == 'development';
|
||||
|
||||
try {
|
||||
// Create & connect db
|
||||
await sequelize.authenticate();
|
||||
@@ -30,15 +36,22 @@ export const connectDB = async () => {
|
||||
|
||||
// Check migrations
|
||||
const pendingMigrations = await umzug.pending();
|
||||
|
||||
if (pendingMigrations.length > 0) {
|
||||
logger.log(`Found pending migrations. Executing...`);
|
||||
|
||||
if (isDev) {
|
||||
pendingMigrations.forEach(({ file }) =>
|
||||
logger.log(`Executing ${file} migration`, 'DEV')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await umzug.up();
|
||||
} catch (err) {
|
||||
logger.log(`Database connection error`, 'ERROR');
|
||||
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
if (isDev) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { DataTypes, Model } from 'sequelize';
|
||||
import type { Migration } from '../';
|
||||
import { DataTypes, Model, QueryInterface } from 'sequelize';
|
||||
import {
|
||||
Snippet,
|
||||
SnippetCreationAttributes
|
||||
@@ -7,16 +6,15 @@ import {
|
||||
|
||||
const { INTEGER, STRING, DATE } = DataTypes;
|
||||
|
||||
export const up: Migration = async ({
|
||||
context: queryInterface
|
||||
}): Promise<void> => {
|
||||
export const up = async (queryInterface: QueryInterface): Promise<void> => {
|
||||
await queryInterface.createTable<Model<Snippet, SnippetCreationAttributes>>(
|
||||
'snippets',
|
||||
{
|
||||
id: {
|
||||
type: INTEGER,
|
||||
allowNull: false,
|
||||
primaryKey: true
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
title: {
|
||||
type: STRING,
|
||||
@@ -38,8 +36,6 @@ export const up: Migration = async ({
|
||||
);
|
||||
};
|
||||
|
||||
export const down: Migration = async ({
|
||||
context: queryInterface
|
||||
}): Promise<void> => {
|
||||
export const down = async (queryInterface: QueryInterface): Promise<void> => {
|
||||
await queryInterface.dropTable('snippets');
|
||||
};
|
||||
|
||||
8
src/middleware/asyncWrapper.ts
Normal file
8
src/middleware/asyncWrapper.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
type Foo = (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
||||
|
||||
export const asyncWrapper =
|
||||
(foo: Foo) => (req: Request, res: Response, next: NextFunction) => {
|
||||
return Promise.resolve(foo(req, res, next)).catch(next);
|
||||
};
|
||||
2
src/middleware/index.ts
Normal file
2
src/middleware/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './asyncWrapper';
|
||||
export * from './requireBody';
|
||||
16
src/middleware/requireBody.ts
Normal file
16
src/middleware/requireBody.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
export const requireBody =
|
||||
(...fields: string[]) =>
|
||||
(req: Request, res: Response, next: NextFunction): void => {
|
||||
const bodyKeys = Object.keys(req.body);
|
||||
const missingKeys: string[] = [];
|
||||
|
||||
fields.forEach(field => {
|
||||
if (!bodyKeys.includes(field)) {
|
||||
missingKeys.push(field);
|
||||
}
|
||||
});
|
||||
|
||||
next();
|
||||
};
|
||||
31
src/models/Snippet.ts
Normal file
31
src/models/Snippet.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Model, DataTypes } from 'sequelize';
|
||||
import { sequelize } from '../db';
|
||||
import { Snippet, SnippetCreationAttributes } from '../typescript/interfaces';
|
||||
|
||||
const { INTEGER, STRING, DATE } = DataTypes;
|
||||
|
||||
interface SnippetInstance
|
||||
extends Model<Snippet, SnippetCreationAttributes>,
|
||||
Snippet {}
|
||||
|
||||
export const SnippetModel = sequelize.define<SnippetInstance>('Snippet', {
|
||||
id: {
|
||||
type: INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true
|
||||
},
|
||||
title: {
|
||||
type: STRING,
|
||||
allowNull: false
|
||||
},
|
||||
language: {
|
||||
type: STRING,
|
||||
allowNull: false
|
||||
},
|
||||
createdAt: {
|
||||
type: DATE
|
||||
},
|
||||
updatedAt: {
|
||||
type: DATE
|
||||
}
|
||||
});
|
||||
1
src/models/index.ts
Normal file
1
src/models/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Snippet';
|
||||
7
src/routes/snippets.ts
Normal file
7
src/routes/snippets.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Router } from 'express';
|
||||
import { createSnippet } from '../controllers/snippets';
|
||||
import { requireBody } from '../middleware';
|
||||
|
||||
export const snippetRouter = Router();
|
||||
|
||||
snippetRouter.route('/').post(requireBody('title', 'language'), createSnippet);
|
||||
@@ -3,16 +3,22 @@ import express from 'express';
|
||||
import { Logger } from './utils';
|
||||
import { connectDB } from './db';
|
||||
|
||||
// Routers
|
||||
import { snippetRouter } from './routes/snippets';
|
||||
|
||||
// Env config
|
||||
dotenv.config({ path: './src/config/.env' });
|
||||
|
||||
const app = express();
|
||||
const logger = new Logger();
|
||||
const logger = new Logger('server');
|
||||
const PORT = process.env.PORT;
|
||||
|
||||
// App config
|
||||
app.use(express.json());
|
||||
|
||||
// Routes
|
||||
app.use('/api/snippets', snippetRouter);
|
||||
|
||||
(async () => {
|
||||
await connectDB();
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
export type ErrorLevel = 'INFO' | 'ERROR' | 'WARN';
|
||||
export type ErrorLevel = 'INFO' | 'ERROR' | 'WARN' | 'DEV';
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { ErrorLevel } from '../typescript/types/ErrorLevel';
|
||||
|
||||
export class Logger {
|
||||
private namespace: string;
|
||||
|
||||
constructor(namespace: string) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public log(message: string, level: ErrorLevel = 'INFO'): void {
|
||||
console.log(`[${this.generateTimestamp()}] [${level}] ${message}`);
|
||||
console.log(
|
||||
`[${this.generateTimestamp()}] [${level}] ${this.namespace}: ${message}`
|
||||
);
|
||||
}
|
||||
|
||||
private generateTimestamp(): string {
|
||||
|
||||
Reference in New Issue
Block a user