I am working on a node server based on express and written in TypeScript 1.7. I'm using some project-specific middlewares, that extend the existing express Request or Response interface, but I can't get it completely working yet (without tsc complaining about not finding X in req or res). I've found other questions about this issue, but they are either out of date (I guess) or the solution is not straight forward.
I took a look at the definition of an existing middleware, but I didn't get it working without manually writing separate d.ts files and referencing them in typings/tsd.d.ts. This is my setup:
// middleware/foobar.ts
declare module Express {
export interface Request {
foobar?: string;
}
}
/* my project-related middleware extends the request by `foobar` */
export = function(req: Express.Request, res, next) {
req.foobar = 'FooBar';
next();
};
// main.ts
import express = require('express');
var app = express();
app.use(require('./middleware/foobar'));
app.get('/foobar', (req, res) => {
/* tsc: Property 'foobar' does not exist on type 'Request' */
res.send(req.foobar);
});
What's the best practice for extending express' Request and Response interfaces? If possible, without the need of writing a separated d.ts, manipulating anything within the typings directory or using /// <reference path="..." /> comments.
I've found a solution myself, using this example as a reference. My middleware needs to be wrapped in a declared module, so middleware/foobar.ts looks like this:
declare module Express {
export interface Request {
foobar?: string;
}
}
declare module 'foobar' {
function foobar(req: Express.Request, res, next) {
req.foobar = 'FooBar';
next();
}
export = foobar;
}
It's even a bit more trickier if you are using classes or other imported stuff within your middleware. In my example, my middleware uses my own "EntityManager" class, which is an abstraction to the database connection (mysql for me). My middleware (it's middleware/database.ts for me) looks like this now:
declare module Express {
import { Manager as EntityManager } from 'entity-manager';
export interface Request {
entityManager?: EntityManager;
}
}
declare module 'database' {
import * as mysql from 'mysql';
import { Manager as EntityManager } from 'entity-manager';
/* some middleware-related code */
var pool = mysql.createPool(...);
var entityManager = new EntityManager(pool);
/* *** */
/* the actual exported function */
function database(req: Express.Request, res, next) {
req.entityManager = entityManager;
next();
};
export = database;
}
Note that my EntityManager class is imported twice, once per module declaration. It did not seem to work by just importing it above both modules.
UPDATE
Having the actual code in a declared module ('database' in my case) produces no output in the JS file.
Having that code within a regular module requires the name not being in apostrophes (i.e. hyphens would not be allowed there for example) and doesn't produce a one-function-export code either.
Having the actual code completely out of a module (so there's only the declared Express module with the extended Request) produces correct JS but my text editor can't find entityManager in req anymore.
It seems like I'm needed to put the typings (my own extensions to Express.Request for example) into a dedicated d.ts file, where no actual code is present.
Related
I'm moving my existing project written on Express.js to Nest.js and one of the most pressing problem is to serve static html page for changing user's password. I've been looking for any answer for a couple of days, unsuccessfully. My implementation on Express.js works perfectly, here it is:
resetPass.use(express.static(__dirname + "/reset_pass_page"));
resetPass.get("/:id", async (req, res) => {
try {
// here I check ID which is JWT and if everything is OK I send the form:
res.status(200).sendFile(__dirname + "/reset_pass_page/index.html");
}
And now I'm trying to reach the same outcome using Nest.js. I got one single module for resetting password and sending links to user's email. Here is the controller:
#Controller('users/resetpass')
export class ResetPassController {
constructor(private readonly resetPassService: ResetPassService) { }
// here is others routes for getting reset link on user's email and etc...
// in this part I'm sending the form:
#Get("requestform/:id")
sendResetPasswordForm(#Param("id") resetToken: string) {
return this.resetPassService.sendResetPasswordForm(resetToken)
}
}
And what should I do in the service in my case?
async sendResetPasswordForm(resetToken: string) {
try {
// checking resetToken and if it's OK send form like:
res.sendFile(__dirname + "/reset_pass_page/index.html");
What method should i use in that case?
}
}
I've already tried to use ServeStaticModule in my reset pass modle, but I can't make it work properly with dynamic routes. I've tried this config:
ServeStaticModule.forRoot({
rootPath: join(__dirname, '../../../static/resetpass'),
renderPath: /(\/users\/resetpass\/requestform\/)([\w-]*\.[\w-]*\.[\w-]*)/g,
}),
I can make it work for routes without ID, like users/resetpass/, but I need to these page be available only for routes like users/resetpass/:id.
I'm looking forward for any help and advice. Thanks!
Similarly to what you did in Express.js:
res.status(200).sendFile(__dirname + "/reset_pass_page/index.html");
You can also use .sendFile in Nest.js
#Get("requestform/:id")
sendResetPasswordForm(#Req() req: Request, #Res() res: Response) {
const resetTokenPath = this.resetPassService.sendResetPasswordForm(pararms.id)
res.sendFile(join(__dirname, resetTokenPath, '/reset_pass_page/index.html'));
}
You have to add a couple of decorators and types from Express:
import { Controller, Get, Res, Req } from '#nestjs/common';
import { Response, Request } from 'express';
I'm starting using VS Code with expressJs. I've decided to split route in different file by using Route.use function.
In the new file I'd like to have intellisense suggesting me all the methods in the app parameters so i've added the /**#param type {Express} app */ jsdoc. The point is that Intellisense isn't able to find type definitions. What do I have to do to let it find type definition for Express?
Here the code i've wrote:
///<reference path="../../node_modules/#types/express/index.d.ts"/>
/**#param {Express} app */
module.exports=function(app){
app.get('/testRoute',function(req,res){
res.send('Hi, I\'m just a simple test');
});
};
Automatic typings acquisition should automatically pick up those types for normal import
or require statements, so in general you should no longer write /// reference path=.
Try something like:
import express from 'express';
module.exports = function (/** #type {express.Express} */ app) {
app.get('/testRoute', function (req, res) {
res.send('Hi, I\'m just a simple test');
});
};
There's currently a bug when using require that prevents types IntelliSense from working properly. This TypeScript issue tracks a possible solution
Supposing we have some middleware in express 4.0:
app.use((req, res, next) => {
// ... i want app here. except this
// method is imported from another file
// so app isn't in scope.
});
Is there any way to get the app object?
I'm writing several custom middleware packages and I keep finding myself needing to reference app (from another file of course). I'm doing hokey things like this:
app.use(fabMiddleware(app));
Which is really a high-order function:
const fabMiddleware = (app) => {
return function(req, res, next) {
// ... now i can use app
}
}
modue.exports = fabMiddleware;
Does perhaps this, req or res have a reference to app?
Yes you can access the app instance without needing to explicitly pass it in. Simply call req.app or res.app to get access to it.
https://expressjs.com/en/4x/api.html#req.app
https://expressjs.com/en/4x/api.html#res.app
I have a single-page application written in ES6. The code in transpiled server-side into classic javascript by babelJs, then loaded by SystemJs.
Javascript present in my html file:
System.config({
baseURL: '/js',
meta: {
'/js/*': { format: 'cjs' }
}});
System.defaultJSExtensions = true;
System.import("index.js")
.catch(function (error) {
console.error(error)
});
index.js:
import f1 from 'file1';
import f2 from 'file2';
// code here ...
Everything works fine. index.js is loaded, and all import statements are correctly executed.
Now, I want to create some pages with mocked ES6 modules, for testing purpose. My goal is to display pages by replacing model classes (contained in ES6 modules) with other static test classes.
Let's say I have 3 files: real_model.js, fake_model.js and component.js. component.js import the real model (import Model from 'real_model';).
How can I replace the real model by the fake one (in the component) dynamically ?
It's been a while since this question was posted, but maybe this solution might still be of help to anyone else.
With SystemJS it is possible to create a module on-the-fly using System.newModule. Then you can use System.set to overwrite existing modules with the new one. In our tests we use the following helper function to mock existing modules:
function mockModule(name, value) {
const normalizedName = System.normalizeSync(name);
System.delete(normalizedName);
System.set(normalizedName, System.newModule(Object.assign({ default: value }, value)));
}
Then, e.g. inside the beforeEach callback, we assign the mock and then import the module to be tested using System.import:
let [component, fake_model] = [];
beforeEach(() => {
// define mock
fake_model = { foo: 'bar' };
// overwrite module with mock
mockModule('real_model', fake_model);
// delete and reimport module
System.delete(System.normalizeSync('component'));
return System.import('src/testing').then((m) => {
component = m.default;
}).catch(err => console.error(err));
});
// test your component here ...
A big advantage of this approach is that you don't need an additional mocking library and it works solely with SystemJS.
I am creating my own package and I want to add Auth only for my package using a different table that the app auth table.
I can't found the way to override the app auth.table config only for my package.
Searching I found this solution, that change the config on the fly.
In my code:
class EasytranslateServiceProvider extends ServiceProvider {
[...]
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot()
{
$this->package('revolistic/easytranslate');
// add the packages routes
include __DIR__.'/../../routes.php';
// doesn't work
$this->app['config']['auth'] = \Config::get('easytranslate::auth');
}
[...]
}
But it doesn't work, look like the Auth module is reading the configuration before the package creation or boot() function call.
If I do:
class EasytranslateServiceProvider extends ServiceProvider {
[...]
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot()
{
$this->package('revolistic/easytranslate');
// add the packages routes
include __DIR__.'/../../routes.php';
// doesn't work
$this->app['config']['auth'] = \Config::get('easytranslate::auth');
// show that the changes was made
print_r($this->app['config']['auth']);
}
[...]
}
I get that the config was changed, but the Auth model is still taking the table name from the app auth config file.
I am using the last version of Laravel, any idea how I can accomplish it?
Thanks in advance
Oh, this solution works i found my error i should to namespace my model in my package auth file like that
'model' => 'Revolistic\Easytranslate\User',
Cheers