I am very new to typescript/javascript, I am trying to build backend rest apis with session
following is app.ts file
import express from "express";
import { applyMiddleware, applyRoutes } from "./utils";
import routes from "./services";
const app = express();
var ses= {
secret: "secret_session",
resave: true,
saveUninitialized: true,
cookie: { maxAge: 3600000,secure: false, httpOnly: true
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1)
ses.cookie.secure = true
}
app.use(session(ses));
applyRoutes(routes, app);
I have started the server and applied the middlewares for error handling but those are not related to question in my opinion so I'm not adding code for it. Following is my routes.ts code where I'm trying to set the session.
import { Request, Response } from "express";
import { getAll, getByKeyword, addNewProduct } from "./productControllers";
{
path: "/api/v1/getAllProducts",
method: "get",
handler: [
(req: Request, res: Response) => {
getAll()
.then((row: any) => {
var sess = req.session;
sess.views = 1;
res.status(200).json({ data: row });
})
.catch(err => {
res.json({
message: err
});
});
}
]
}
I'm getting error at sess.views = 1;
I have tried the suggested questions before asking it, none of them were of any help to me.
EDIT:
I have created an index.ts
import searchRoutes from "./products/routes";
export default [...searchRoutes];
I have another util class
export const applyRoutes = (routes: Route[], router: Router) => {
for (const route of routes) {
const { method, path, handler } = route;
(router as any)[method](path, handler);
}
}
You are using an interface which is Request for express.js. But it doesn't have type definition for session. So typescript throws a compile error. To solve it you need to define session type under Request interface.
You could define a session.d.ts file under your project. And create required types & interfaces. Like:
declare global {
namespace Express {
interface Request {
session?: Session;
sessionID?: string;
}
}
}
interface Session{
mySessionVarible:string
}
But the good thing is we have DefinitilyTyped project which you can find many type definitions. This needs to solve your compile problem.
npm install --save-dev #types/express-session
And don't forget to change your import for Request.
import { Request, Response } from "#types/express-session";
Related
I will just say first of all that I am aware of almost all the questions asked on this site under this title.
The solutions there were pretty obvious and already done by me (with no success) or only helped for those specific cases and didn’t really work in my case unfortunately.
Now, for the problem:
I'm trying to create a route that will handle a get request and a post request which are sent to the route 'ellipses'.
These requests should receive and send data from and to an SQL database.
The problem is that for some reason the router is not ready to get these functions and gives me the error in the title:
Router.use () requires middleware function but got an undefined
Here is my code:
This code is from the file dat.js. its porpose is just to access the SQL database.
import { Sequelize } from "sequelize";
export const sequelize = new Sequelize('TheDataBaseName', 'TheUser', 'ThePassword', {
host: 'localhost',
dialect: 'mssql'
});
This code is from the file: controller.js. its porpose is to manage the requests and load the data.
import { sequelize } from "../dat";
export const sendEllipses = async (req, res, next) => {
try {
const ellipses = await getEllipsesFromJson();
return res.send(ellipses);
} catch (e) {
console.log(e);
}
};
export const addNewEllipse = async (req, res, next) => {
const { body: obj } = req;
let newEllipse;
try {
if (Object.keys(obj) !== null) {
logger.info(obj);
newEllipse = await sequelize.query(
`INSERT INTO [armageddon].[dbo].[ellipses] (${Object.keys(
obj
).toString()})
values (${Object.values(obj).toString()})`
);
} else {
console.log("the values are null or are empty");
}
return res.send(newEllipse);
} catch (error) {
console.log(error);
}
};
This code is on the file: routers.js.
its porpose is to define the route
import Router from "express";
import { sendEllipses } from "../ellipses.controller";
import { addNewEllipse } from "../ellipses.controller";
const router = Router();
export default router.route("/ellipses").get(sendEllipses).post(addNewEllipse);
This code is from the file: app.js. This is where everything actually happens.
import { router } from "../routers";
import express from "express";
app.use('/api', router);
app.listen(5000, () => {
console.log("server is runing on port 5000")
});
You need to export the router
const router = Router();
router.route("/ellipses").get(sendEllipses).post(addNewEllipse)
export default router
Now import the router:
import routes from "../router.js";
app.use('/api', routes);
Its also mentioned in the docs: https://expressjs.com/de/guide/routing.html#express-router
I want to use routing-controllers package in my app. But when I add a middleware I get an error.
Here is my code:
index.ts:
import "reflect-metadata";
import { createExpressServer } from "routing-controllers";
import { UserController } from "./UserController";
const app = createExpressServer({
defaultErrorHandler: false,
controllers: [UserController],
});
app.use((req, res, next) => {
res.status(404).send("Not Found!");
});
app.listen(3000, () => {
console.log("********listening**********");
});
UserController.ts:
import { Controller, Get } from "routing-controllers";
#Controller("/users")
export class UserController {
#Get("/")
getAll() {
return "This action returns all users";
}
}
I get "cannot set headers after they are sent to the client error" when I send a request to the "/users/" endpoint. Normally the request finish and the last middleware do not run. Am i wrong?
Im currently trying to learn more about Express and Express-Validator but currently facing the following issue: When I'm starting the server and using Postman do excess one of the endpoints the response is not completed. However, it seems like the Validation Chain is being processed but afterwards nothing happens. I have the following modules:
index.ts
import {config} from 'dotenv';
import App from './api/app';
import ControlAmbilight from './api/routes/controlAmbilight';
import AdjustLightning from './app/AdjustLightning';
const ENV_FILE = path.join(__dirname, '..', '.env');
config({path: ENV_FILE});
const PORT = process.env.port || process.env.PORT || 3000;
const ambient = new AdjustLightning();
const app = new App([
new ControlAmbilight(ambient),
], <number> PORT);
app.listen();
app.ts
import * as bodyParser from 'body-parser';
import pino from 'pino';
import expressPino from 'express-pino-logger';
import errorMiddleware from './middleware/errorMiddleware';
export default class App {
private logger: pino.Logger;
private expressLogger;
public app: express.Application;
public port: number;
constructor(controllers: any, port: number) {
this.app = express();
this.port = port;
this.logger = pino({level: process.env.LOG_LEVEL || 'info'});
this.expressLogger = expressPino({logger: this.logger});
this.initializeMiddlewares();
this.initializeControllers(controllers);
this.initializeErrorHandling();
}
private initializeMiddlewares() {
this.app.use(this.expressLogger);
this.app.use(bodyParser.json());
}
private initializeControllers(controllers: any) {
controllers.forEach((controller: any) => {
this.app.use('/', controller.router);
});
}
private initializeErrorHandling() {
this.app.use(errorMiddleware)
}
public listen() {
this.app.listen(this.port, () => {
this.logger.info(`Server running on ${this.port}`);
});
}
}
controlAmbilight.ts
import {Router, Request, Response, NextFunction} from 'express';
import {ValidationChain, check, validationResult} from 'express-validator';
import AdjustLightning from '../../app/AdjustLightning';
// eslint-disable-next-line require-jsdoc
export default class ControlAmbilight {
private ambient: AdjustLightning;
// eslint-disable-next-line new-cap
public router = Router();
public path = '/controlAmbilight';
// eslint-disable-next-line require-jsdoc
constructor(ambient: AdjustLightning) {
this.ambient = ambient;
this.initializeRoutes();
}
// eslint-disable-next-line require-jsdoc
public initializeRoutes() {
this.router.post(this.path, this.controlValidator, this.setAmbilight.bind(this));
}
private controlValidator = (): ValidationChain[] => [
check('on').notEmpty().withMessage('Field \'on\' is required'),
check('on').isBoolean().withMessage('Field \'on\' must be type boolean'),
];
// eslint-disable-next-line require-jsdoc
private setAmbilight(req: Request, res: Response): void {
const errors = validationResult(req);
if (!errors.isEmpty()) {
res.status(422).json({error: errors.array()});
} else {
const isOn = (req.body.on == 'true');
res.send(`The curent state is: ${this.ambient.getIsActive()}`);
}
}
}
I was hopping that someone could explain me what I'm missing here. It seems like I need to call express` next() middleware function, but I'm not sure where to implement it.
EDIT
As requested I'm adding the errorMiddleware:
import { NextFunction, Request, Response } from 'express';
import HttpException from '../exceptions/HttpException';
export default function errorMiddleware (error: HttpException,
request: Request, response: Response, next: NextFunction) {
const status = error.status || 500;
const message = error.message || 'Ups... This did not work :(';
response
.status(status)
.send({ status,
message });
}
And as an additional comment: When I'm adding the Validation Chain directly into the post method within controlAmbilight.ts like that:
public initializeRoutes() {
this.router.post(this.path, [
check('on').notEmpty().withMessage('Field \'on\' is required'),
check('on').isBoolean().withMessage('Field \'on\' must be type boolean'),
], this.setAmbilight.bind(this));
}
It is working as expected.
I'm working (and learning) on my TypeScript skills, although I ran into a problem: I have a class named Manager which contains and manages multiple 'sub' managers. In the index file, I load the Manager by creating an instance and calling the load function. When loading all 'sub' managers get a reference to the main/only Manager instance, this way they can call/use the other 'sub' managers.
But I would like to be able to get some info from the 'sub' managers at a REST API endpoint. These endpoints are loaded through routes:
index.ts
import "reflect-metadata";
import { createConnection } from "typeorm";
import { Request, Response } from "express";
import * as express from "express";
import * as bodyParser from "body-parser";
import { AppRoutes } from "./routes";
import { Manager } from "./manager";
createConnection().then(async (typeORMConnection) => {
const manager = new Manager();
manager.load().then(() => {
console.log("Manager has loaded all managers");
const expressApp = express();
expressApp.use(bodyParser.json());
expressApp.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "*");
res.header("Access-Control-Allow-Headers", "*");
next();
});
// Loop over every route
AppRoutes.forEach((singleRoute) => {
// Generate Express route
expressApp[singleRoute.method](singleRoute.path, (request: Request, response: Response, next: Function) => {
singleRoute.action(request, response)
.then(() => next())
.catch((error) => next(error));
});
});
// Start Express app
expressApp.listen(3000);
console.log("Express application is up and running on port 3000");
});
}).catch((error) => console.log(`TypeORM connection error: ${error}`));
A route file looks like this:
routes.ts
import { getSpeakerById, getSpeakerAll } from "./controller/get";
import { enableSpeakerById, disableSpeakerById } from "./controller/put";
export const AppRoutes = [
{
path: "/speaker",
method: "get",
action: getSpeakerAll
},
{
path: "/speaker/:id",
method: "get",
action: getSpeakerById
},
{
path: "/speaker/:id/disable",
method: "put",
action: disableSpeakerById
},
{
path: "/speaker/:id/enable",
method: "put",
action: enableSpeakerById
},
];
And last but not least this is an Express endpoint file containing the actual logic:
controller/get.ts
import { Request, Response } from "express";
import { getManager } from "typeorm";
import { Speaker } from "../entity/Speaker";
const ping = require("ping");
export async function getSpeakerById(request: Request, response: Response) {
const speakerRepository = getManager().getRepository(Speaker);
const speakerObject = await speakerRepository.findOne(request.params.id);
// If no post is found return 404
if (!speakerObject) {
response.status(404);
response.send("Speaker doesn't exist");
response.end();
return;
}
// Ping speaker and bind the time once its been resolved
speakerObject.time = await ping.promise.probe(speakerObject.host);
response.send(speakerObject);
}
export async function getSpeakerAll(request: Request, response: Response) {
const speakerRepository = getManager().getRepository(Speaker);
const speakerObjects = await speakerRepository.find();
const speakerPromise = [];
// Create a promise array of pings to all speakers
speakerObjects.forEach((speakerObject) => speakerPromise.push(ping.promise.probe(speakerObject.host)));
const speakerResults = await Promise.all(speakerPromise);
// Since the promise array is based on order we can rebind the property by looping over it in the same order
speakerResults.forEach((speakerResult, speakerIndex) => speakerObjects[speakerIndex].time = speakerResult.time);
response.send(speakerObjects);
}
Now I need to access the main Manager instance in the controller/get.ts, but I can't pass it along as parameter (for as far as I know) since it's an export. I would just import the Manager class and create a new instance but I only want to start the Manager once since it contains logic such as intervals and speaker instances from the Sonos package. I hope I was able to explain the problem but if anyone needs clarification on something I'll update the post.
You actually can pass it along as a parameter. There's nothing stopping you doing something like this in your index.ts:
// Generate Express route
expressApp[singleRoute.method](singleRoute.path, (request: Request, response: Response, next: Function) => {
singleRoute.action(request, response, manager)
.then(() => next())
.catch((error) => next(error));
});
});
And then updating the signature on you exported controller methods to be like
import { Manager } from '../manager';
export async function getSpeakerById(request: Request, response: Response, manager: Manager) {
...
}
I have the following express server:
import * as express from "express";
import * as bodyParser from "body-parser";
import * as mongoose from "mongoose";
import { Routes } from "./routes/transactions";
import { authMiddleware } from "./middleware/auth";
import { errorMiddleware } from "./middleware/error";
class App {
public app: express.Application;
public routeProvider: Routes = new Routes();
public mongoUrl: string = "mongodb://...:...#mydomain.com:27017/mycollection"; // This should be read from github CLI
constructor() {
this.app = express();
this.config();
this.routeProvider.routes(this.app);
this.mongoSetup();
}
private config(): void {
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));
this.app.use(errorMiddleware);
this.app.use(authMiddleware);
}
private mongoSetup(): void {
mongoose.Promise = global.Promise;
mongoose.connect(
this.mongoUrl,
{ useNewUrlParser: true }
);
}
}
export default new App().app;
This is the authMiddleware (almost the same as the guide on the auth0 site for usage with node.js):
import * as jwt from "express-jwt";
import { expressJwtSecret } from "jwks-rsa";
export function authMiddleware() {
return jwt({
secret: expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: "..."
}),
audience: "...",
issuer: "...",
algorithms: ["RS256"]
});
}
And my error middleware:
import { Request, Response } from "express";
export function errorMiddleware(err, req: Request, res: Response, next) {
console.error(err.stack);
res.status(500).send("Something broke!");
}
Now what would expect to happen - since I broke all the auth and mongodb URLs on purpose, that I would get a status code 500 with the message Something broke!.
Instead I get the
Could not get any response
message when sending a requst using postman.
What am I doing wrong?