Alright so I am trying to understand Node.js and Typescript, so I tried working on a simple script which is as follows;
app.ts
import * as express from "express";
import * as bodyParser from "body-parser";
import { Routes } from "./routes/crm_routes";
class App {
public app;
public routePrv : Routes = new Routes();
constructor() {
this.app = express();
this.routePrv.routes(this.app);
this.config();
}
private config():void {
this.app.use(bodyParser.json);
this.app.use(bodyParser.urlencoded({ extended: false }));
}
}
export default new App().app;
./lib/routes/crm_routes.ts
import {Request, Response} from "express";
export class Routes {
public routes(app): void {
app.route('/').get((req, res) => res.json({name : "ohmygodnotthisagain"}));
}
}
server.ts
import app from "./app";
app.listen(3000, () => console.log('Example app listening on port 3000!'));
Now I was playing around so I put this.config() above this.routePrv.routes(this.app), and my server stopped routing to / altogether.
When I put them back in the above order it started working again.
Now I tried to understand the cause of this, and am confused, is it because body-parser needs to be the last middleware called such that auth, extra checks, etc middleware complete working or are there something else?
Any and all help will be appreciated. Thanks!
PS: I'm pretty new to TS, pointers would be great.
Body parser (or the middleware in general) should be called before the actual route.
Your route is not working because you have a spelling error here:
this.app.use(bodyParser.json);
Should be:
this.app.use(bodyParser.json());
The route works when you put that code last, because its never actually executed (the route gets matched first and stops the execution, since you are not calling the next() function)
Related
I'm attempting to dynamically import files containing routes for an Express server using ES6 notation instead of associating each route with a file like this:
import router from './routes/test.mjs'
app.use('/api/create/test', router)
In my main file App.js I have a function which aims to return the matching :entity import from the query parameters:
async function routingFunct(req, res, next){
let entity=req.params.entity
const { default: dynamicImport } = await import(`./routes/${entity}.mjs`);
return dynamicImport
}
app.use('/api/create/:entity', routingFunct)
In my ./routes/test.mjs file I have
router.get('/', async function(req, res)
{
...await logic here...
})
export default router
When sending a get request to the endpoint /api/create/test, the page simply hangs forever. What am I doing wrong here? How can I tell my Express server to run the middleware in App.js then run the logic in the matching :entity.mjs?
I am building an application backend in TypeScript & NodeJS.
I have kept multiple routes logic in separate files(in single folder for each service) as :
> signup.ts // export { router as signupRouter };
> signin.ts // export { router as signinRouter };
> signout.ts // export { router as signoutRouter };
I need to do this for all the services in future.
I need to combine these routes so that I can specify them as a single entity in the index.ts file.
Something like:
import <object_that_combines_all> from './routes/<some_file_for_object>';
app.use('/api/users', require('./routes/users/<object_that_combines_all>'));
.
.
and so on
So that I don't have to mention each route explicitly in the index.ts file.
There is a simpler way using the router.use() method in which we can combine the individual endpoints.
Assuming your file structure looks something like this -
/src
/routes
/users
- signup.ts
- signin.ts
- signout.ts
index.ts (your main app entry point)
You can utilize the router.use() method provided by express.Router().
First, add a new index.ts file to the /routes/users folder to combine the multiple endpoints.
// /routes/users/index.ts
import * as express from 'express';
import { signupRouter } from './signupRouter';
import { signinRouter } from './signinRouter';
import { signoutRouter } from './signoutRouter';
const router = express.Router();
router.use(signupRouter);
router.use(signinRouter);
router.use(signoutRouter);
export {router as usersRouter};
Then in your main app entry point index.ts, do the following -
// /src/index.ts
import * as express from 'express';
import { usersRouter } from './routes/users/index.ts';
const PORT = process.env.PORT || 4000;
const app = express();
...
// Add your single entity entry point like this
app.use('/api/user', usersRouter);
...
app.listen(PORT, () => console.log(`Listening on port ${PORT}`)
To do that, you'll need to dynamically discover the filenames (readdir or similar) and use dynamic import to import them. Dynamic import (import()) returns a promise of the module namespace object for a module.
For instance, using the fs/promises version of readdir, something along these lines (off-the-cuff, some tweaking probably required):
import { readdir } from "fs/promises";
const directoryPath = "/path/to/the/files";
const rexIsRouterFile = /^.+\.ts$/i;
async function loadRoutes(app) {
const names = await readdir(directoryPath);
for (const name of names.filter(name => rexIsRouteFile.test(name))) {
const { router } of await import(`${directoryPath}/${name}`);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^−−−−−−− dynamic import
app.use('/api/users', router);
}
}
Note that this assumes you have a TypeScript loader setup (for instance, as with ts-node). If you don't, you'll need to compile those TypeScript files to JavaScript before this runs, then change .ts to .js in rexIsRouterFile.
I use TypeScript for my Express app. I distributed the registration of certain endpoints across several other modules. These modules import main.ts and register them (see depFoo.ts). Unfortunately they don't seem to be registered and because app starts listening before deppFoo.ts executed it's registration!?
What is a proper way in TypeScript to fix this Heg-Enn problem? Can I register a callback that gets executed once TypeScript resolved all modules?
main.ts
depFoo.ts
main.ts
export const app: express.Application = express();
app.listen(3000, function () {
console.log("App is listening on port 3000!");
});
depFoo.ts
import { app } import "./main.ts"
app.get(...);
This tutorial gives a great explanation of the router style system with express TS. One of the cleaner ways to do it is to have a routes directory with files similar to
import * as express from "express";
export const register = ( app: express.Application ) => {
app.get( "/", ( req: any, res ) => {
res.render( "index" );
} );
};
and then in your main app file you can use
import * as routes from './routes'
...
const app = express()
routes.register(app)
Which makes your main file a whole lot cleaner and your endpoints more modular.
I'm new to nodeJS, I'm trying to follow this tutorial.
My code:
// server/index.js
import express from 'express';
import { graphqlExpress, graphiqlExpress } from 'graphql-server-express';
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools';
import bodyParser from 'body-parser';
import { createServer } from 'http';
import { Schema } from './data/schema';
import { Mocks } from './data/mocks';
const GRAPHQL_PORT = 8000;
const app = express();
const executableSchema = makeExecutableSchema({
typeDefs: Schema,
});
addMockFunctionsToSchema({
schema: executableSchema,
mocks: Mocks,
preserveResolvers: true,
});
// `context` must be an object and can't be undefined when using connectors
app.use('/graphql', bodyParser.json(), graphqlExpress({
schema: executableSchema,
context: {}, // at least(!) an empty object
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
}));
const graphQLServer = createServer(app);
graphQLServer.listen(GRAPHQL_PORT, () => console.log(`GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}/graphql`));
reports an error Cannot GET /
I've read that maybe the createServer function is deprecated, but I'm not sure how to fix it.
When you use express, you have to be explicitly define the routes used by your application. For example, if you define a route with app.get('/hello', handler) then any GET requests to localhost/hello will get routed to that handler. It can then execute whatever logic and return a response, be that a JSON object, a webpage, etc.
Express will only handle the routes you've defined in this way. So if you've only defined a route for requests that GET /hello, it will not know how to GET /foo, or GET your root path /. If you wanted to implement a way to POST or PUT to /hello, that would need to be a different route as well.
You can use app.use in a similar way to implement middleware in your application. While middleware typically takes your request, manipulates it and pass it on, it can also be used to break up your routing logic.
In the case of GraphQL, requests are typically made using the POST method, but the specification does allow for both POST and GET requests. To do this, we would have to define handlers for both app.get('/graphql') and app.post('/graphql'). The graphqlExpress middleware you're importing and using conveniently does that for you.
So with your set up, you've created some routes that allow you to POST to and GET from localhost:8000/graphql. You've also enabled GraphiQL on localhost:8000/graphiql. If you don't see any errors in the console when you start your server, you should be able to navigate to the GraphiQL page at localhost:8000/graphiql and play around with your schema.
But these are the only routes you've set up on your server. If you attempt to navigate anywhere else, like the root at localhost:8000/, express won't know how to handle that request and you will see the error you reported.
Use app.listen(port, function(){console.log('server started')}); instead of createServer(app).
I'm trying to load ExpressJS routes from a Typescript class. The idea is that this should eventually be dynamic routes. But I'm already stuck at defininghard coded routes in a class.
My index.ts js looks like this:
import generic = require('./generic');
import Generic = generic.Generic;
class Test {
private app:any;
private port:number = 3000;
constructor() {
this.app = express();
new Generic();
this.app.listen(this.port, () => {
console.log('Listening on port ' + this.port);
});
}
}
export = Test;
new Test();
Then my generic.ts looks like this. I'm trying to define another route from this class:
module Generic {
export class Generic {
constructor() {
console.log('In generic');
var router:any = express.Router();
router.get('/', function (req, res) {
res.setHeader('Content-Type', 'application/json');
res.status(200).send("AAA");
});
}
}
}
export = Generic;
When I run my application then I see the message In generic appear in console. But when I load my browser it says:
Cannot GET /
So for some reason it's not really registering the route from the Generic class. What am I doing wrong?
Take a look at the sample on Microsoft's github page. Rather than using express.Router, they simply use get and import the function from an external module. It has worked pretty well for me.
app.ts
import * as express from "express";
import * as routes from "./routes/index";
var app = express();
app.get('/', routes.index);
routes/index.ts
import express = require("express")
export function index(req: express.Request, res: express.Response) {
res.render('index', { title: 'ImageBoard'});
};