Here's a high level view of a controller in Typescript-Node :
As I'm storing details of user in product model, I have used a middleware to check if user if logged in before accessing the endpoint and also injecting user info to the req which can be further used in different controllers
exports.addProduct = async (req: Request, res: Response, next: NextFunction) => {
// images:
try {
// logic to handle data from req.body
// getting this user id from middleware isLoggedIn
// injecting user id into request in the isLoggedIn middleware
req.body.user = req.user._id;
const product = await Product.create(req.body);
return res.status(200).json({
success: true,
product,
});
} catch (error) {
logger.error(error);
}
};
Getting error : Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' , on the line
req.body.user = req.user._id;
isLoggedIn is typical function to check Bearer token or header or cookies and then inject user info to the request
It worked Perfectly in Javascript, now trying same in Typescript as a part to learn Typescipt
There are two ways to achieve this:
Extending express Request locally
Extending express Request globally
Using the local way require to write lots of redundent code and that's why
the global way is much better. it can be done by creating file as follows:
index.d.ts
import { User } from "../../models/user";
// to make the file a module and avoid the TypeScript error
export {};
declare global {
namespace Express {
export interface Request {
user: User;
}
}
}
Then add this config to tsconfig.json file
"typeRoots": [
"src/#types",
"./node_modules/#types",
],
Then Request object will recognize user and user can be injected from any middleware to be used in any controller.
The problem is that according to the typing of req, there is no property named user. TypeScript is notifying you that req.user should be undefined, according to the available typings. There are some possible solutions to fix your problem.
You could explicitly type the variable as any. This is considered to be bad practice sometimes, because in general you should try to type everything correctly (nevertheless: it works).
// Option 1: Explicitly declare variable as any
req.body.user = (req as any).user._id;
You could also check if req.user is defined, like this:
// Option 2: Check req.user manually
if (req.user) req.body.user = req.user._id;
else throw new Error("Some Error");
You could also type the req correctly, according to the API specifications of your middleware. This is usually a lot of work if done manually. Some modules ship with correct TypeScript-typings already.
Maybe you want to also look into this question since it is very similar to your question.
first create a folder call types it should be at the root of your project
then at yow tsconfig.json in the compilerOptions section add a paths prop
{
"compilerOptions": {
...
"paths": {
"express": [
"./types/express/index.d.ts"
],
}
}
then at the types dir add a new dir call express inside add an index.d.ts go ahead a copy them express definitions
// Type definitions for Express 4.17
// Project: http://expressjs.com
// Definitions by: Boris Yankov <https://github.com/borisyankov>
// China Medical University Hospital <https://github.com/CMUH>
// Puneet Arora <https://github.com/puneetar>
// Dylan Frankland <https://github.com/dfrankland>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/* =================== USAGE ===================
import express = require("express");
var app = express();
=============================================== */
/// <reference types="express-serve-static-core" />
/// <reference types="serve-static" />
import * as bodyParser from 'body-parser';
import * as serveStatic from 'serve-static';
import * as core from 'express-serve-static-core';
import * as qs from 'qs';
/**
* Creates an Express application. The express() function is a top-level function exported by the express module.
*/
declare function e (): core.Express;
declare namespace e {
/**
* This is a built-in middleware function in Express. It parses incoming requests with JSON payloads and is based on body-parser.
* #since 4.16.0
*/
var json: typeof bodyParser.json;
/**
* This is a built-in middleware function in Express. It parses incoming requests with Buffer payloads and is based on body-parser.
* #since 4.17.0
*/
var raw: typeof bodyParser.raw;
/**
* This is a built-in middleware function in Express. It parses incoming requests with text payloads and is based on body-parser.
* #since 4.17.0
*/
var text: typeof bodyParser.text;
/**
* These are the exposed prototypes.
*/
var application: Application;
var request: Request;
var response: Response;
/**
* This is a built-in middleware function in Express. It serves static files and is based on serve-static.
*/
var static: serveStatic.RequestHandlerConstructor<Response>;
/**
* This is a built-in middleware function in Express. It parses incoming requests with urlencoded payloads and is based on body-parser.
* #since 4.16.0
*/
var urlencoded: typeof bodyParser.urlencoded;
/**
* This is a built-in middleware function in Express. It parses incoming request query parameters.
*/
export function query (options: qs.IParseOptions | typeof qs.parse): Handler;
export function Router (options?: RouterOptions): core.Router;
interface RouterOptions {
/**
* Enable case sensitivity.
*/
caseSensitive?: boolean | undefined;
/**
* Preserve the req.params values from the parent router.
* If the parent and the child have conflicting param names, the child’s value take precedence.
*
* #default false
* #since 4.5.0
*/
mergeParams?: boolean | undefined;
/**
* Enable strict routing.
*/
strict?: boolean | undefined;
}
interface SessionData {
userIp: string;
ipDetails: any;
publicKey: string;
session: string;
iv: string;
decrypted: any;
}
interface Application extends core.Application { }
interface CookieOptions extends core.CookieOptions { }
interface Errback extends core.Errback { }
interface ErrorRequestHandler<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, any> = Record<string, any>
> extends core.ErrorRequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> { }
interface Express extends core.Express { }
interface Handler extends core.Handler { }
interface IRoute extends core.IRoute { }
interface IRouter extends core.IRouter { }
interface IRouterHandler<T> extends core.IRouterHandler<T> { }
interface IRouterMatcher<T> extends core.IRouterMatcher<T> { }
interface MediaType extends core.MediaType { }
interface NextFunction extends core.NextFunction { }
interface Request<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, any> = Record<string, any>
> extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> { }
interface RequestHandler<
P = core.ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = core.Query,
Locals extends Record<string, SessionData> = Record<string, SessionData>
> extends core.RequestHandler<P, ResBody, ReqBody, ReqQuery, Locals> { }
interface RequestParamHandler extends core.RequestParamHandler { }
export interface Response<ResBody = any, Locals extends Record<string, SessionData> = Record<string, SessionData>>
extends core.Response<ResBody, Locals> { }
interface Router extends core.Router { }
interface Send extends core.Send { }
}
export = e;
if you notice from the above I added an interface call SessionData if you look almost at the end I set Locals to be equals to it. at the Response
now at yow endPoint you can apply it like this
import type e from "express";
export const endPoint: e.RequestHandler = (req, res, next) => {
//code
};
you can go beong that you can also add them params if there are any, the res body, the req body and stuff
const endPoint: e.RequestHandler<YowParamsObj,YowResBodyObj,YowReqBodyObj,ThemQueryParamsObj> = (req, res, next) => {
//code
};
Related
I seem to have an issue with Typescript typings on my Express Request object. The project for now exists out of 2 sub-projects (user-service and a common project which includes reusable Errors and Middlewares)
The common folder is installed as a dependency in the user-service like:
"#myPackage/common": "file:../common",
In there I have a current-user middleware:
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
interface UserPayload {
id: string;
email: string;
}
declare global {
namespace Express {
interface Request {
currentUser?: UserPayload;
}
}
}
const currentUser = (
req: Request,
res: Response,
next: NextFunction,
) => {
if (!req.session?.jwt) {
return next();
}
try {
const payload = jwt.verify(
req.session.jwt,
process.env.JWT_KEY!,
) as UserPayload;
req.currentUser = payload;
} catch (err) {
console.error(err);
}
return next();
};
export default currentUser;
with a declared global for the currentUser property on the Request object.
In my user-service project I have the following route
import express, { Request, Response } from 'express';
import { Middlewares } from '#myPackage/common';
const router = express.Router();
router.get('/api/users/currentuser', Middlewares.currentUser, (
req: Request,
res: Response,
) => {
res.send({ currentUser: req.currentUser || null });
});
export default router;
On req.currentUser I get the following error message:
Property 'currentUser' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Shouldn't the package typings automatically be taken over in the code in which you import it? I hope I made myself clear on what the problem is :)
I've also always had trouble declaring a global namespace to attach types to express's request object. Found myself a solution using "declare module" instead of "declare global". So instead of
declare global {
namespace Express {
interface Request {
currentUser?: UserPayload;
}
}
}
maybe give the following approach a try:
declare module "express-serve-static-core" {
interface Request {
currentUser?: UserPayload;
}
}
Normally your currentUser property should also be available in other files with this approach, but you can of course export the manipulated Request interface if not.
Note that in most cases you will need to reference the "express-serve-static-core" module as this is where the Request interface is declared.
I was trying to create an endpoint in node.js, more specifically in express
but I am not sure why I can't access userService variable when requesting from a client.
I've gotten Cannot read property 'userService' of undefined, but when i move ServicesFactory.getInstance().getUserService() inside the signUp function it works?!
I am guessing that node.js garbage collects it due to it's not being used until the user make a request.
export class UserApi implements WebEndpoint {
router: Router
userService = ServicesFactory.getInstance().getUserService()
constructor() {
this.router = Router()
this.router.post('/signup', this.signUp)
}
signUp(req: Request, res: Response): void {
const user: User = req.body
this.userService.signUp(user)
res.send("Successfully registered")
}
}
I found the problem, so basically I am a noob.
consider this example
class a {
constructor() {
this.a1 = 'hello';
}
greet(){
const greeting = `${this.a1} dude!`;
console.log(greeting);
};
}
class b {
b1 = new a();
constructor() {
this.b1.greet.call();
}
}
new b();
Now it wouldn't run, because b class called greet method with a new context, the same with express when you provide a function as a handler on an Express endpoint it will be called with a new set of context (read:this) that's why this.userService in my code above won't work because there is no userService property in the context provided by Express.
The solution is simple. Arrow function.
signUp = (req: Request, res: Response): void => {
const user: User = req.body
this.userService.signUp(user)
res.send("Successfully registered")
}
Now the function will inherit it's class's context.You can refer to this for more detail answer.
I am trying to add correlationId using express-correlation-id. I am exactly following the page: https://www.npmjs.com/package/express-correlation-id. I've imported the express-correlation-id pkg and have found it in the package.json and node modules.
But when i tried to get req.correlationId(), it always said:
Property 'correlationId' dosn't exist on type 'Request<ParamsDictionary, any, any, ParsedQs>'.
I'm using TypeScript and Express, this is what the code looks like:
import correlator = require("express-correlation-id");
app.use(correlator());
app.get('/', (req, res) => {
req.correlationId(); // where the error occurs
});
Since this package is written in js language, there are no type definition files for typescript, so you need to extend the Express.Request interface by yourself, add the correlationId method to the interface.
Package versions:
"typescript": "^3.9.7"
"express-correlation-id": "^1.3.1",
"express": "^4.17.1",
E.g.
server.ts:
import express from 'express';
import correlator = require('express-correlation-id');
declare global {
namespace Express {
export interface Request {
correlationId(): string;
}
}
}
const app = express();
app.use(correlator());
app.get('/', (req, res) => {
req.correlationId();
});
This is because the property correlationId doesn't exist in the typing of req.
So, if you see the typing of req is:
export interface Request<P = ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = ParsedQs> extends http.IncomingMessage, Express.Request {
So, you must create a new Request type with the new functions that you're going to use. For your case:
import express from "express";
import correlator = require("express-correlation-id");
import { ParamsDictionary, Request } from "express-serve-static-core";
import { ParsedQs } from "qs";
const app = express();
interface CustomReq<P = ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = ParsedQs> extends Request<P, ResBody, ReqBody, ReqQuery> {
// extended options
correlationId: () => any;
}
app.use(correlator());
app.get('/', (req: CustomReq, res) => {
req.correlationId(); // OK
});
As you see in the code above, my new Request is CustomReq and I use the new type for the req.
I have a URL which looks like http://www.example.com/idf34he8sf/9iad2hf7usnf. I want to get the params idf34he8sf and 9iad2hf7usnf
I have used below code
In angular
this.route.paramMap.subscribe(params => {
this.organizationId = params.get("organizationId");
this.embedId= params.get("embedId");
}
In Node
req.params
and
req.originalUrl
I want to get the params idf34he8sf and 9iad2hf7usnf
You have to define params in your api as-
example/:organizationId/:embedId
then fetch these params using-
constructor(private router: Router) {
this.organizationId = this.router.snapshots.params['organizationId']
this.embedId = this.router.snapshots.params['embedId']
}
Node knows nothing about Angular, you will need to design your API to take those paramaters and pass them when calling the API.
You can use #angular/router
import { Router } from '#angular/router';
Define a variable to hold URL
href:string;
params:string[];
Then in Constructor
constructor(private router: Router) {
}
In ngOnInit
this.href = this.router.url;
this.params = this.href.split('/');
In angular you can use ActivatedRoute to fetch params from the URL
import { Router, ActivatedRoute} from '#angular/router';
constructor(route: ActivatedRoute){}
/* Do Something*/
public someFunction(){
this.route.queryParams.subscribe(params => {
/* use Params*/
}
}
In NodeJS you will need to add while declaring your routes as shown below:
router.get('/:id', (req,res) =>{
let id = req.params; // for parameterised routes
// fetching query string use below:
// region = req.query.region;
return res.json(id);
});
//import the ActivatedRoute to use route.snapshot.params
import { ActivatedRoute} from '#angular/router';
//define a variable to store the param from url
public param:any;
//And in constructor create a instance of ActivatedRoute
constructor(private route:ActivatedRoute,) {}
//Then in ngOnInit
ngOnInit(): void {
this.param = this.route.snapshot.params["Your-params-name in url)"];
// params-name means (/:id/)
//this will store the params from your url to the variable param (public)
}
//in my url is I wanted to store specific id of a book
// from url localhost:4200/books/60ab539f678gfraep/
// I used
{path: 'books/:id',component: showBookComponent},
//in my ROUTING MODULE
//so I gave above code as
public Id:string;
ngOnInit():void {
// this.Id = route.snapshot.params['id'];
}
How do you access the url query params in nodejs with typescript?
What I tried is here?
/**
* My Server app
*/
import * as Http from "http";
import * as url from "url";
import { HttpServer } from "./HttpServer";
import { TaxCalculator } from "./TaxCalculator";
export interface myReq extends Http.IncomingMessage {
amount: string;
rate: string;
}
/**
* MyServer class
* Create MyServer from HttpServer
*/
class MyServer extends HttpServer {
/**
* Create a new NodeServer object
* #param port Port number of the server
* #param name Name of the server
*/
constructor(port: number, name: string) {
super(port, name);
}
/**
* Handle the Request in request nad populate the response to response
* #param request Incoming Request
* #param response Outgoing Response
*/
onRequest(request: myReq, response: Http.ServerResponse): void {
response.writeHead(200, {"Content-Type": "text/plain"});
// const { amount, rate } = url.parse(request.url, true).query;
const query = url.parse(request.url, true).query;
const tc = new TaxCalculator();
const tax = tc.calculate(query.amount, query.rate);
response.end(JSON.stringify(tax));
}
}
// Create a server instance
const port = 8080;
new MyServer(port, "Test server");
The error is Argument of type 'string | string[]' is not assignable to parameter of type 'n
umber'.
Inside your route handling middleware there are the req, res, next parameters of the router callback. You are looking for req.query.[your query param].
For typescript, you'll want to create an interface that extends express.Request and add your query param there. Then assign that type to your request param.
interface myReq extends express.Request {
query: {
[Whatever params you have in your route]: string;
}
}
router.get("/", (req: myReq, res, next) => {
...
})
Youll also have to import express in the file if you havent already