diff --git a/express-main-example/.gitignore b/express-main-example/.gitignore index b622412..31b5726 100644 --- a/express-main-example/.gitignore +++ b/express-main-example/.gitignore @@ -1,2 +1,3 @@ node_modules *.sqlite +dist diff --git a/express-main-example/express/routes/instruments.js b/express-main-example/express/routes/instruments.js deleted file mode 100644 index 7e56a08..0000000 --- a/express-main-example/express/routes/instruments.js +++ /dev/null @@ -1,60 +0,0 @@ -const { models } = require('../../sequelize'); -const { getIdParam } = require('../helpers'); - -async function getAll(req, res) { - const instruments = await models.instrument.findAll(); - res.status(200).json(instruments); -}; - -async function getById(req, res) { - const id = getIdParam(req); - const instrument = await models.instrument.findByPk(id); - if (instrument) { - res.status(200).json(instrument); - } else { - res.status(404).send('404 - Not found'); - } -}; - -async function create(req, res) { - if (req.body.id) { - res.status(400).send(`Bad request: ID should not be provided, since it is determined automatically by the database.`) - } else { - await models.instrument.create(req.body); - res.status(201).end(); - } -}; - -async function update(req, res) { - const id = getIdParam(req); - - // We only accept an UPDATE request if the `:id` param matches the body `id` - if (req.body.id === id) { - await models.instrument.update(req.body, { - where: { - id: id - } - }); - res.status(200).end(); - } else { - res.status(400).send(`Bad request: param ID (${id}) does not match body ID (${req.body.id}).`); - } -}; - -async function remove(req, res) { - const id = getIdParam(req); - await models.instrument.destroy({ - where: { - id: id - } - }); - res.status(200).end(); -}; - -module.exports = { - getAll, - getById, - create, - update, - remove, -}; diff --git a/express-main-example/express/routes/orchestras.js b/express-main-example/express/routes/orchestras.js deleted file mode 100644 index af2b02b..0000000 --- a/express-main-example/express/routes/orchestras.js +++ /dev/null @@ -1,64 +0,0 @@ -const { models } = require('../../sequelize'); -const { getIdParam } = require('../helpers'); - -async function getAll(req, res) { - const orchestras = 'includeInstruments' in req.query ? - await models.orchestra.findAll({ include: models.instrument }) : - await models.orchestra.findAll(); - res.status(200).json(orchestras); -}; - -async function getById(req, res) { - const id = getIdParam(req); - const orchestra = 'includeInstruments' in req.query ? - await models.orchestra.findByPk(id, { include: models.instrument }) : - await models.orchestra.findByPk(id); - if (orchestra) { - res.status(200).json(orchestra); - } else { - res.status(404).send('404 - Not found'); - } -}; - -async function create(req, res) { - if (req.body.id) { - res.status(400).send(`Bad request: ID should not be provided, since it is determined automatically by the database.`) - } else { - await models.orchestra.create(req.body); - res.status(201).end(); - } -}; - -async function update(req, res) { - const id = getIdParam(req); - - // We only accept an UPDATE request if the `:id` param matches the body `id` - if (req.body.id === id) { - await models.orchestra.update(req.body, { - where: { - id: id - } - }); - res.status(200).end(); - } else { - res.status(400).send(`Bad request: param ID (${id}) does not match body ID (${req.body.id}).`); - } -}; - -async function remove(req, res) { - const id = getIdParam(req); - await models.orchestra.destroy({ - where: { - id: id - } - }); - res.status(200).end(); -}; - -module.exports = { - getAll, - getById, - create, - update, - remove, -}; diff --git a/express-main-example/express/routes/users.js b/express-main-example/express/routes/users.js deleted file mode 100644 index f87f8e7..0000000 --- a/express-main-example/express/routes/users.js +++ /dev/null @@ -1,60 +0,0 @@ -const { models } = require('../../sequelize'); -const { getIdParam } = require('../helpers'); - -async function getAll(req, res) { - const users = await models.user.findAll(); - res.status(200).json(users); -}; - -async function getById(req, res) { - const id = getIdParam(req); - const user = await models.user.findByPk(id); - if (user) { - res.status(200).json(user); - } else { - res.status(404).send('404 - Not found'); - } -}; - -async function create(req, res) { - if (req.body.id) { - res.status(400).send(`Bad request: ID should not be provided, since it is determined automatically by the database.`) - } else { - await models.user.create(req.body); - res.status(201).end(); - } -}; - -async function update(req, res) { - const id = getIdParam(req); - - // We only accept an UPDATE request if the `:id` param matches the body `id` - if (req.body.id === id) { - await models.user.update(req.body, { - where: { - id: id - } - }); - res.status(200).end(); - } else { - res.status(400).send(`Bad request: param ID (${id}) does not match body ID (${req.body.id}).`); - } -}; - -async function remove(req, res) { - const id = getIdParam(req); - await models.user.destroy({ - where: { - id: id - } - }); - res.status(200).end(); -}; - -module.exports = { - getAll, - getById, - create, - update, - remove, -}; diff --git a/express-main-example/index.js b/express-main-example/index.js deleted file mode 100644 index 94640fe..0000000 --- a/express-main-example/index.js +++ /dev/null @@ -1,27 +0,0 @@ -const app = require('./express/app'); -const sequelize = require('./sequelize'); -const PORT = 8080; - -async function assertDatabaseConnectionOk() { - console.log(`Checking database connection...`); - try { - await sequelize.authenticate(); - console.log('Database connection OK!'); - } catch (error) { - console.log('Unable to connect to the database:'); - console.log(error.message); - process.exit(1); - } -} - -async function init() { - await assertDatabaseConnectionOk(); - - console.log(`Starting Sequelize + Express example on port ${PORT}...`); - - app.listen(PORT, () => { - console.log(`Express server started on port ${PORT}. Try some routes, such as '/api/users'.`); - }); -} - -init(); diff --git a/express-main-example/package.json b/express-main-example/package.json index 302b680..127d937 100644 --- a/express-main-example/package.json +++ b/express-main-example/package.json @@ -1,20 +1,26 @@ { - "name": "express-main-example", - "version": "0.0.0", - "description": "This is an example of how to setup Sequelize and Express together in a project for NodeJS 10 and above.", - "main": "index.js", - "scripts": { - "start": "node index.js", - "setup-example-db": "node sqlite-example-database/setup.js" - }, - "engines": { - "node": ">=10" - }, - "license": "MIT", - "dependencies": { - "body-parser": "^1.19.0", - "express": "^4.17.1", - "sequelize": "^6.3.3", - "sqlite3": "^5.0.0" - } + "name": "express-main-example", + "version": "0.0.0", + "description": "This is an example of how to setup Sequelize and Express together in a project for NodeJS 10 and above.", + "main": "index.js", + "scripts": { + "start": "tsc-watch --onSuccess \"node dist/index.js\"", + "setup-example-db": "tsc && node dist/sqlite-example-database/setup.js" + }, + "engines": { + "node": ">=10" + }, + "license": "MIT", + "dependencies": { + "body-parser": "^1.19.0", + "express": "^4.17.1", + "sequelize": "^6.3.3", + "sqlite3": "^5.0.0" + }, + "devDependencies": { + "@types/body-parser": "^1.19.0", + "@types/express": "^4.17.11", + "tsc-watch": "^4.2.9", + "typescript": "^4.2.3" + } } diff --git a/express-main-example/sequelize/extra-setup.js b/express-main-example/sequelize/extra-setup.js deleted file mode 100644 index 7d8fc6f..0000000 --- a/express-main-example/sequelize/extra-setup.js +++ /dev/null @@ -1,8 +0,0 @@ -function applyExtraSetup(sequelize) { - const { instrument, orchestra } = sequelize.models; - - orchestra.hasMany(instrument); - instrument.belongsTo(orchestra); -} - -module.exports = { applyExtraSetup }; diff --git a/express-main-example/sequelize/index.js b/express-main-example/sequelize/index.js deleted file mode 100644 index c0f9ee4..0000000 --- a/express-main-example/sequelize/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const { Sequelize } = require('sequelize'); -const { applyExtraSetup } = require('./extra-setup'); - -// In a real app, you should keep the database connection URL as an environment variable. -// But for this example, we will just use a local SQLite database. -// const sequelize = new Sequelize(process.env.DB_CONNECTION_URL); -const sequelize = new Sequelize({ - dialect: 'sqlite', - storage: 'sqlite-example-database/example-db.sqlite', - logQueryParameters: true, - benchmark: true -}); - -const modelDefiners = [ - require('./models/user.model'), - require('./models/instrument.model'), - require('./models/orchestra.model'), - // Add more models here... - // require('./models/item'), -]; - -// We define all models according to their files. -for (const modelDefiner of modelDefiners) { - modelDefiner(sequelize); -} - -// We execute any extra setup after the models are defined, such as adding associations. -applyExtraSetup(sequelize); - -// We export the sequelize connection instance to be used around our app. -module.exports = sequelize; diff --git a/express-main-example/sequelize/models/instrument.model.js b/express-main-example/sequelize/models/instrument.model.js deleted file mode 100644 index 0bbad3a..0000000 --- a/express-main-example/sequelize/models/instrument.model.js +++ /dev/null @@ -1,33 +0,0 @@ -const { DataTypes } = require('sequelize'); - -// We export a function that defines the model. -// This function will automatically receive as parameter the Sequelize connection object. -module.exports = (sequelize) => { - sequelize.define('instrument', { - // The following specification of the 'id' attribute could be omitted - // since it is the default. - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER - }, - type: { - allowNull: false, - type: DataTypes.STRING, - }, - // type: { - // allowNull: false, - // type: DataTypes.STRING, - // validate: { - // isIn: [['string', 'wind', 'percussion']] - // } - // }, - purchaseDate: { - allowNull: false, - type: DataTypes.DATE - }, - // We also want it to have a 'orchestraId' field, but we don't have to define it here. - // It will be defined automatically when Sequelize applies the associations. - }); -}; diff --git a/express-main-example/sequelize/models/orchestra.model.js b/express-main-example/sequelize/models/orchestra.model.js deleted file mode 100644 index b6f06f9..0000000 --- a/express-main-example/sequelize/models/orchestra.model.js +++ /dev/null @@ -1,20 +0,0 @@ -const { DataTypes } = require('sequelize'); - -// We export a function that defines the model. -// This function will automatically receive as parameter the Sequelize connection object. -module.exports = (sequelize) => { - sequelize.define('orchestra', { - // The following specification of the 'id' attribute could be omitted - // since it is the default. - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER - }, - name: { - allowNull: false, - type: DataTypes.STRING - }, - }); -}; diff --git a/express-main-example/sequelize/models/user.model.js b/express-main-example/sequelize/models/user.model.js deleted file mode 100644 index f1f5118..0000000 --- a/express-main-example/sequelize/models/user.model.js +++ /dev/null @@ -1,26 +0,0 @@ -const { DataTypes } = require('sequelize'); - -// We export a function that defines the model. -// This function will automatically receive as parameter the Sequelize connection object. -module.exports = (sequelize) => { - sequelize.define('user', { - // The following specification of the 'id' attribute could be omitted - // since it is the default. - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER - }, - username: { - allowNull: false, - type: DataTypes.STRING, - unique: true, - validate: { - // We require usernames to have length of at least 3, and - // only use letters, numbers and underscores. - is: /^\w{3,}$/ - } - }, - }); -}; diff --git a/express-main-example/sqlite-example-database/helpers/random.js b/express-main-example/sqlite-example-database/helpers/random.js deleted file mode 100644 index aa8dd82..0000000 --- a/express-main-example/sqlite-example-database/helpers/random.js +++ /dev/null @@ -1,9 +0,0 @@ -function pickRandom(args) { - return args[Math.floor(Math.random() * args.length)]; -} - -function randomDate() { - return new Date(new Date() - 200000000000 * Math.random()); -} - -module.exports = { pickRandom, randomDate }; diff --git a/express-main-example/sqlite-example-database/setup.js b/express-main-example/sqlite-example-database/setup.js deleted file mode 100644 index a480043..0000000 --- a/express-main-example/sqlite-example-database/setup.js +++ /dev/null @@ -1,53 +0,0 @@ -const sequelize = require('../sequelize'); -const { pickRandom, randomDate } = require('./helpers/random'); - -async function reset() { - console.log('Will rewrite the SQLite example database, adding some dummy data.'); - - await sequelize.sync({ force: true }); - - await sequelize.models.user.bulkCreate([ - { username: 'jack-sparrow' }, - { username: 'white-beard' }, - { username: 'black-beard' }, - { username: 'brown-beard' }, - ]); - - await sequelize.models.orchestra.bulkCreate([ - { name: 'Jalisco Philharmonic' }, - { name: 'Symphony No. 4' }, - { name: 'Symphony No. 8' }, - ]); - - // Let's create random instruments for each orchestra - for (const orchestra of await sequelize.models.orchestra.findAll()) { - for (let i = 0; i < 10; i++) { - const type = pickRandom([ - 'violin', - 'trombone', - 'flute', - 'harp', - 'trumpet', - 'piano', - 'guitar', - 'pipe organ', - ]); - - await orchestra.createInstrument({ - type: type, - purchaseDate: randomDate() - }); - - // The following would be equivalent in this case: - // await sequelize.models.instrument.create({ - // type: type, - // purchaseDate: randomDate(), - // orchestraId: orchestra.id - // }); - } - } - - console.log('Done!'); -} - -reset(); diff --git a/express-main-example/express/app.js b/express-main-example/src/express/app.ts similarity index 77% rename from express-main-example/express/app.js rename to express-main-example/src/express/app.ts index c3821b5..adb79de 100644 --- a/express-main-example/express/app.js +++ b/express-main-example/src/express/app.ts @@ -1,22 +1,28 @@ -const express = require('express'); -const bodyParser = require('body-parser'); +import express, { NextFunction, Request, Response } from "express"; +import { json, urlencoded } from "body-parser"; const routes = { - users: require('./routes/users'), - instruments: require('./routes/instruments'), - orchestras: require('./routes/orchestras'), + users: require("./routes/users"), + instruments: require("./routes/instruments"), + orchestras: require("./routes/orchestras"), // Add more routes here... // items: require('./routes/items'), }; const app = express(); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: true })); +app.use(json()); +app.use(urlencoded({ extended: true })); + +type Handler = ( + req?: Request, + res?: Response, + next?: NextFunction +) => Promise | void; // We create a wrapper to workaround async errors not being transmitted correctly. -function makeHandlerAwareOfAsyncErrors(handler) { - return async function(req, res, next) { +function makeHandlerAwareOfAsyncErrors(handler: Handler) { + return async function (req: Request, res: Response, next: NextFunction) { try { await handler(req, res); } catch (error) { @@ -26,7 +32,7 @@ function makeHandlerAwareOfAsyncErrors(handler) { } // We provide a root route just as an example -app.get('/', (req, res) => { +app.get("/", (req: Request, res: Response) => { res.send(`

Hello, Sequelize + Express!

Make sure you have executed npm run setup-example-db once to have a populated example database. Otherwise, you will get 'no such table' errors.

@@ -69,4 +75,4 @@ for (const [routeName, routeController] of Object.entries(routes)) { } } -module.exports = app; +export default app; diff --git a/express-main-example/express/helpers.js b/express-main-example/src/express/helpers.ts similarity index 78% rename from express-main-example/express/helpers.js rename to express-main-example/src/express/helpers.ts index d0b303f..f3836ca 100644 --- a/express-main-example/express/helpers.js +++ b/express-main-example/src/express/helpers.ts @@ -1,11 +1,11 @@ +import { Request } from "express"; + // A helper function to assert the request ID param is valid // and convert it to a number (since it comes as a string by default) -function getIdParam(req) { +export function getIdParam(req: Request) { const id = req.params.id; if (/^\d+$/.test(id)) { return Number.parseInt(id, 10); } throw new TypeError(`Invalid ':id' param: "${id}"`); } - -module.exports = { getIdParam }; diff --git a/express-main-example/src/express/routes/instruments.ts b/express-main-example/src/express/routes/instruments.ts new file mode 100644 index 0000000..6294616 --- /dev/null +++ b/express-main-example/src/express/routes/instruments.ts @@ -0,0 +1,61 @@ +import { Request, Response } from "express"; +import { Instrument } from "../../sequelize/models/instrument.model"; +import { getIdParam } from "../helpers"; + +export async function getAll(req: Request, res: Response) { + const instruments = await Instrument.findAll(); + res.status(200).json(instruments); +} + +export async function getById(req: Request, res: Response) { + const id = getIdParam(req); + const instrument = await Instrument.findByPk(id); + if (instrument) { + res.status(200).json(instrument); + } else { + res.status(404).send("404 - Not found"); + } +} + +export async function create(req: Request, res: Response) { + if (req.body.id) { + res + .status(400) + .send( + `Bad request: ID should not be provided, since it is determined automatically by the database.` + ); + } else { + await Instrument.create(req.body); + res.status(201).end(); + } +} + +export async function update(req: Request, res: Response) { + const id = getIdParam(req); + + // We only accept an UPDATE request if the `:id` param matches the body `id` + if (req.body.id === id) { + await Instrument.update(req.body, { + where: { + id, + }, + }); + res.status(200).end(); + } else { + res + .status(400) + .send( + `Bad request: param ID (${id}) does not match body ID (${req.body.id}).` + ); + } +} + +export async function remove(req: Request, res: Response) { + const id = getIdParam(req); + await Instrument.destroy({ + where: { + id, + }, + }); + res.status(200).end(); +} diff --git a/express-main-example/src/express/routes/orchestras.ts b/express-main-example/src/express/routes/orchestras.ts new file mode 100644 index 0000000..eefd111 --- /dev/null +++ b/express-main-example/src/express/routes/orchestras.ts @@ -0,0 +1,68 @@ +import { Request, Response } from "express"; +import { Instrument } from "../../sequelize/models/instrument.model"; +import { Orchestra } from "../../sequelize/models/orchestra.model"; +import { getIdParam } from "../helpers"; + +export async function getAll(req: Request, res: Response) { + const orchestras = + "includeInstruments" in req.query + ? await Orchestra.findAll({ include: Instrument }) + : await Orchestra.findAll(); + res.status(200).json(orchestras); +} + +export async function getById(req: Request, res: Response) { + const id = getIdParam(req); + const orchestra = + "includeInstruments" in req.query + ? await Orchestra.findByPk(id, { include: Instrument }) + : await Orchestra.findByPk(id); + if (orchestra) { + res.status(200).json(orchestra); + } else { + res.status(404).send("404 - Not found"); + } +} + +export async function create(req: Request, res: Response) { + if (req.body.id) { + res + .status(400) + .send( + `Bad request: ID should not be provided, since it is determined automatically by the database.` + ); + } else { + await Orchestra.create(req.body); + res.status(201).end(); + } +} + +export async function update(req: Request, res: Response) { + const id = getIdParam(req); + + // We only accept an UPDATE request if the `:id` param matches the body `id` + if (req.body.id === id) { + await Orchestra.update(req.body, { + where: { + id, + }, + }); + res.status(200).end(); + } else { + res + .status(400) + .send( + `Bad request: param ID (${id}) does not match body ID (${req.body.id}).` + ); + } +} + +export async function remove(req: Request, res: Response) { + const id = getIdParam(req); + await Orchestra.destroy({ + where: { + id, + }, + }); + res.status(200).end(); +} diff --git a/express-main-example/src/express/routes/users.ts b/express-main-example/src/express/routes/users.ts new file mode 100644 index 0000000..8bcfaea --- /dev/null +++ b/express-main-example/src/express/routes/users.ts @@ -0,0 +1,61 @@ +import { Request, Response } from "express"; +import { User } from "../../sequelize/models/user.model"; +import { getIdParam } from "../helpers"; + +export async function getAll(req: Request, res: Response) { + const users = await User.findAll(); + res.status(200).json(users); +} + +export async function getById(req: Request, res: Response) { + const id = getIdParam(req); + const user = await User.findByPk(id); + if (user) { + res.status(200).json(user); + } else { + res.status(404).send("404 - Not found"); + } +} + +export async function create(req: Request, res: Response) { + if (req.body.id) { + res + .status(400) + .send( + `Bad request: ID should not be provided, since it is determined automatically by the database.` + ); + } else { + await User.create(req.body); + res.status(201).end(); + } +} + +export async function update(req: Request, res: Response) { + const id = getIdParam(req); + + // We only accept an UPDATE request if the `:id` param matches the body `id` + if (req.body.id === id) { + await User.update(req.body, { + where: { + id: id, + }, + }); + res.status(200).end(); + } else { + res + .status(400) + .send( + `Bad request: param ID (${id}) does not match body ID (${req.body.id}).` + ); + } +} + +export async function remove(req: Request, res: Response) { + const id = getIdParam(req); + await User.destroy({ + where: { + id: id, + }, + }); + res.status(200).end(); +} diff --git a/express-main-example/src/index.ts b/express-main-example/src/index.ts new file mode 100644 index 0000000..0a4470a --- /dev/null +++ b/express-main-example/src/index.ts @@ -0,0 +1,34 @@ +import app from "./express/app"; + +// it is important to import sequelize at least once in the app, +// as this will create the database connection, and also create the relations +// between all models. +import sequelize from "./sequelize"; + +const PORT = 8080; + +async function assertDatabaseConnectionOk() { + console.log(`Checking database connection...`); + try { + await sequelize.authenticate(); + console.log("Database connection OK!"); + } catch (error) { + console.log("Unable to connect to the database:"); + console.log(error.message); + process.exit(1); + } +} + +async function init() { + await assertDatabaseConnectionOk(); + + console.log(`Starting Sequelize + Express example on port ${PORT}...`); + + app.listen(PORT, () => { + console.log( + `Express server started on port ${PORT}. Try some routes, such as '/api/users'.` + ); + }); +} + +init(); diff --git a/express-main-example/src/sequelize/connection.ts b/express-main-example/src/sequelize/connection.ts new file mode 100644 index 0000000..f6640dc --- /dev/null +++ b/express-main-example/src/sequelize/connection.ts @@ -0,0 +1,13 @@ +import { Sequelize } from "sequelize"; + +// In a real app, you should keep the database connection URL as an environment variable. +// But for this example, we will just use a local SQLite database. +// const sequelize = new Sequelize(process.env.DB_CONNECTION_URL); +const sequelize = new Sequelize({ + dialect: "sqlite", + storage: "sqlite-example-database/example-db.sqlite", + logQueryParameters: true, + benchmark: true, +}); + +export default sequelize; diff --git a/express-main-example/src/sequelize/extra-setup.ts b/express-main-example/src/sequelize/extra-setup.ts new file mode 100644 index 0000000..7f93b23 --- /dev/null +++ b/express-main-example/src/sequelize/extra-setup.ts @@ -0,0 +1,7 @@ +import { Instrument } from "./models/instrument.model"; +import { Orchestra } from "./models/orchestra.model"; + +export function applyExtraSetup() { + Orchestra.hasMany(Instrument); + Instrument.belongsTo(Orchestra); +} diff --git a/express-main-example/src/sequelize/index.ts b/express-main-example/src/sequelize/index.ts new file mode 100644 index 0000000..09488d5 --- /dev/null +++ b/express-main-example/src/sequelize/index.ts @@ -0,0 +1,13 @@ +import { applyExtraSetup } from "./extra-setup"; + +import sequelize from "./connection"; + +// must reference all models, after importing the sequelize connection. +import "./models/instrument.model"; +import "./models/orchestra.model"; +import "./models/user.model"; + +// We execute any extra setup after the models are defined, such as adding associations. +applyExtraSetup(); + +export default sequelize; diff --git a/express-main-example/src/sequelize/models/instrument.model.ts b/express-main-example/src/sequelize/models/instrument.model.ts new file mode 100644 index 0000000..8757ac6 --- /dev/null +++ b/express-main-example/src/sequelize/models/instrument.model.ts @@ -0,0 +1,49 @@ +import { DataTypes, ModelDefined, Optional } from "sequelize"; +import sequelize from "../connection"; + +interface InstrumentAttributes { + id: number; + type: string; + purchaseDate: Date; + + // We need to specify the 'orchestraId' field here, + // because it will automatically be defined later on + // when Sequelize applies the associations + orchestraId: number; +} + +// we can make the 'id' field optional, since we aren't required +// to set it manually when creating a new entry +interface InstrumentCreationAttributes + extends Optional {} + +export const Instrument: ModelDefined< + InstrumentAttributes, + InstrumentCreationAttributes +> = sequelize.define("instrument", { + // The following specification of the 'id' attribute could be omitted + // since it is the default. + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + type: { + allowNull: false, + type: DataTypes.STRING, + }, + // type: { + // allowNull: false, + // type: DataTypes.STRING, + // validate: { + // isIn: [['string', 'wind', 'percussion']] + // } + // }, + purchaseDate: { + allowNull: false, + type: DataTypes.DATE, + }, + // We also want it to have a 'orchestraId' field, but we don't have to define it here. + // It will be defined automatically when Sequelize applies the associations. +}); diff --git a/express-main-example/src/sequelize/models/orchestra.model.ts b/express-main-example/src/sequelize/models/orchestra.model.ts new file mode 100644 index 0000000..5d948ac --- /dev/null +++ b/express-main-example/src/sequelize/models/orchestra.model.ts @@ -0,0 +1,30 @@ +import { DataTypes, ModelDefined, Optional } from "sequelize"; +import sequelize from "../connection"; + +interface OrchestraAttributes { + id: number; + name: string; +} + +// we can make the 'id' field optional, since we aren't required +// to set it manually when creating a new entry +interface OrchestraCreationAttributes + extends Optional {} + +export const Orchestra: ModelDefined< + OrchestraAttributes, + OrchestraCreationAttributes +> = sequelize.define("orchestra", { + // The following specification of the 'id' attribute could be omitted + // since it is the default. + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + name: { + allowNull: false, + type: DataTypes.STRING, + }, +}); diff --git a/express-main-example/src/sequelize/models/user.model.ts b/express-main-example/src/sequelize/models/user.model.ts new file mode 100644 index 0000000..ac87f98 --- /dev/null +++ b/express-main-example/src/sequelize/models/user.model.ts @@ -0,0 +1,35 @@ +import { DataTypes, ModelDefined, Optional } from "sequelize"; +import sequelize from "../connection"; + +interface UserAttributes { + id: number; + username: string; +} + +// we can make the 'id' field optional, since we aren't required +// to set it manually when creating a new entry +interface UserCreationAttributes extends Optional {} + +export const User: ModelDefined< + UserAttributes, + UserCreationAttributes +> = sequelize.define("user", { + // The following specification of the 'id' attribute could be omitted + // since it is the default. + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER, + }, + username: { + allowNull: false, + type: DataTypes.STRING, + unique: true, + validate: { + // We require usernames to have length of at least 3, and + // only use letters, numbers and underscores. + is: /^\w{3,}$/, + }, + }, +}); diff --git a/express-main-example/src/sqlite-example-database/helpers/random.ts b/express-main-example/src/sqlite-example-database/helpers/random.ts new file mode 100644 index 0000000..887c88a --- /dev/null +++ b/express-main-example/src/sqlite-example-database/helpers/random.ts @@ -0,0 +1,7 @@ +export function pickRandom(args: string[]) { + return args[Math.floor(Math.random() * args.length)]; +} + +export function randomDate() { + return new Date(+new Date() - 200000000000 * Math.random()); +} diff --git a/express-main-example/src/sqlite-example-database/setup.ts b/express-main-example/src/sqlite-example-database/setup.ts new file mode 100644 index 0000000..32d94dd --- /dev/null +++ b/express-main-example/src/sqlite-example-database/setup.ts @@ -0,0 +1,52 @@ +import sequelize from "../sequelize"; +import { Instrument } from "../sequelize/models/instrument.model"; +import { Orchestra } from "../sequelize/models/orchestra.model"; +import { User } from "../sequelize/models/user.model"; +import { pickRandom, randomDate } from "./helpers/random"; + +async function reset() { + console.log( + "Will rewrite the SQLite example database, adding some dummy data." + ); + + await sequelize.sync({ force: true }); + + await User.bulkCreate([ + { username: "jack-sparrow" }, + { username: "white-beard" }, + { username: "black-beard" }, + { username: "brown-beard" }, + ]); + + await Orchestra.bulkCreate([ + { name: "Jalisco Philharmonic" }, + { name: "Symphony No. 4" }, + { name: "Symphony No. 8" }, + ]); + + // Let's create random instruments for each orchestra + for (const orchestra of await Orchestra.findAll()) { + for (let i = 0; i < 10; i++) { + const type = pickRandom([ + "violin", + "trombone", + "flute", + "harp", + "trumpet", + "piano", + "guitar", + "pipe organ", + ]); + + await Instrument.create({ + type, + purchaseDate: randomDate(), + orchestraId: orchestra.get().id, + }); + } + } + + console.log("Done!"); +} + +reset(); diff --git a/express-main-example/tsconfig.json b/express-main-example/tsconfig.json new file mode 100644 index 0000000..3fe40d2 --- /dev/null +++ b/express-main-example/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "rootDir": "src", + "outDir": "dist" + } +}