new todo-backend
This commit is contained in:
43
todo-backend/src/Server.ts
Normal file
43
todo-backend/src/Server.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import cookieParser from "cookie-parser";
|
||||
import express, { Request, Response } from "express";
|
||||
import "express-async-errors";
|
||||
import cors from "cors";
|
||||
import { Sequelize } from "sequelize";
|
||||
|
||||
import BaseRouter from "./routes";
|
||||
import { dbConnectionOptions } from "./entities/db";
|
||||
|
||||
const app = express();
|
||||
|
||||
// DO NOT USE in production as this allows any site to use our backend
|
||||
// you will need to configure cors separately for your application
|
||||
app.use(cors());
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
|
||||
const sequelize = new Sequelize(dbConnectionOptions);
|
||||
sequelize
|
||||
.authenticate()
|
||||
.then(() => {
|
||||
console.log("Connection has been established successfully.");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Unable to connect to the database:", err);
|
||||
});
|
||||
|
||||
// Add APIs
|
||||
app.use("/api", BaseRouter);
|
||||
|
||||
// Print API errors
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
app.use((err: Error, req: Request, res: Response) => {
|
||||
console.error(err, true);
|
||||
return res.status(500).json({
|
||||
error: err.message,
|
||||
});
|
||||
});
|
||||
|
||||
// Export express instance
|
||||
export default app;
|
||||
54
todo-backend/src/entities/Item.ts
Normal file
54
todo-backend/src/entities/Item.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Sequelize, DataTypes, Model, Optional } from "sequelize";
|
||||
import { dbConnectionOptions } from "./db";
|
||||
|
||||
export interface TodoItemAttributes {
|
||||
id: number;
|
||||
description: string;
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
type TodoItemCreationAttributes = Optional<TodoItemAttributes, "id">;
|
||||
|
||||
interface TodoItemInstance
|
||||
extends Model<TodoItemAttributes, TodoItemCreationAttributes> {
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
const sequelize = new Sequelize(dbConnectionOptions);
|
||||
|
||||
export const TodoItem = sequelize.define<TodoItemInstance>(
|
||||
"TodoItem",
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.BIGINT,
|
||||
primaryKey: true,
|
||||
unique: true,
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
},
|
||||
description: { type: DataTypes.STRING, allowNull: true },
|
||||
done: { type: DataTypes.BOOLEAN, allowNull: true },
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
freezeTableName: true,
|
||||
}
|
||||
);
|
||||
|
||||
// (re-)creates table (NOT database)
|
||||
if (process.env.DATABASE_INIT === "true") {
|
||||
TodoItem.sync({ force: true });
|
||||
}
|
||||
|
||||
export async function createTodoItem(todoItem: TodoItemCreationAttributes) {
|
||||
return TodoItem.create(todoItem).then((item) => item.get());
|
||||
}
|
||||
|
||||
export async function deleteTodoItem(id: number) {
|
||||
return TodoItem.destroy({ where: { id } });
|
||||
}
|
||||
|
||||
export async function listAllTodoItems(): Promise<TodoItemAttributes[]> {
|
||||
return TodoItem.findAll({}).then((items) => items.map((i) => i.get()));
|
||||
}
|
||||
18
todo-backend/src/entities/db.ts
Normal file
18
todo-backend/src/entities/db.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Options } from "sequelize";
|
||||
|
||||
const {
|
||||
DATABASE_NAME,
|
||||
DATABASE_USER,
|
||||
DATABASE_PASSWORD,
|
||||
DATABASE_SVC,
|
||||
DATABASE_PORT,
|
||||
} = process.env;
|
||||
|
||||
export const dbConnectionOptions: Options = {
|
||||
database: DATABASE_NAME ?? "todo",
|
||||
username: DATABASE_USER ?? "root",
|
||||
password: DATABASE_PASSWORD ?? "",
|
||||
host: DATABASE_SVC ?? "localhost",
|
||||
port: Number(DATABASE_PORT) ?? 3306,
|
||||
dialect: "mysql",
|
||||
};
|
||||
7
todo-backend/src/index.ts
Normal file
7
todo-backend/src/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import app from "@server";
|
||||
|
||||
// Start the server
|
||||
const port = Number(process.env.PORT || 8080);
|
||||
app.listen(port, () => {
|
||||
console.log("Express server started on port: " + port);
|
||||
});
|
||||
27
todo-backend/src/routes/Items.ts
Normal file
27
todo-backend/src/routes/Items.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {
|
||||
createTodoItem,
|
||||
deleteTodoItem,
|
||||
listAllTodoItems,
|
||||
} from "@entities/Item";
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export function handleCreate(req: Request, res: Response) {
|
||||
const { description, done } = req.body;
|
||||
|
||||
// NOTE production applications should use a validation framework
|
||||
if (typeof description !== "string" || typeof done !== "boolean") {
|
||||
res
|
||||
.status(400)
|
||||
.send("required parameters were either missing or the wrong type");
|
||||
} else {
|
||||
createTodoItem({ description, done }).then((item) => res.json(item));
|
||||
}
|
||||
}
|
||||
|
||||
export function handleReadAll(req: Request, res: Response) {
|
||||
listAllTodoItems().then((items) => res.json(items));
|
||||
}
|
||||
|
||||
export function handleDelete(req: Request, res: Response) {
|
||||
deleteTodoItem(Number(req.params.id)).then(() => res.json({}));
|
||||
}
|
||||
13
todo-backend/src/routes/index.ts
Normal file
13
todo-backend/src/routes/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Router } from "express";
|
||||
import { handleCreate, handleReadAll, handleDelete } from "./Items";
|
||||
|
||||
// Item routes
|
||||
const itemRouter = Router();
|
||||
itemRouter.get("/", handleReadAll);
|
||||
itemRouter.post("/", handleCreate);
|
||||
itemRouter.delete("/:id", handleDelete);
|
||||
|
||||
// Export the base-router
|
||||
const baseRouter = Router();
|
||||
baseRouter.use("/items", itemRouter);
|
||||
export default baseRouter;
|
||||
Reference in New Issue
Block a user