I have this directory structure:
- pages
- api
- products
- pro
- [id].js
- add.js
[id].js
import { upload, baseHandler } from 'helpers';
import { sequelize } from 'lib';
import { init, Suppliers, Products} from 'models';
import { supplierController, productController } from 'controllers';
const handler = baseHandler.get( async (req, res) => {
const {
query: { id, name },
method,
} = req
init()
res.status(400).json(id)
})
export default handler;
export const config = {
api: {
bodyParser: false,
},
};
The static routes works fine but not the dynamic ones. I get this error when I try to access them
error - Error: getaddrinfo ENOTFOUND api.example.com
which means Next.js does not add dynamic routes from the file system.
Related
I am trying to test some API endpoints without actually hitting the database (happens when using supertest). So I am trying to stub/fake the routes and just force a return. The other issue is if I wanted to mock the internal methods that are in the routes, I am having problems doing that as well.
The API is structured like this:
controller.js
export const route = "/named-route";
export const controller = new Router();
controller.get("/:id", async (req, res, next) => {
try {
const post = await getPost(req);
res.status(200).json(post);
} catch (err) {
next(err);
}
});
router.js
import {
controller as myController,
route as myRoute
} from "./controller.js";
router.use(myRoute, myController);
const router = Router();
export default router;
app.js
import router from "./router.js";
const app = express();
app.use(router);
export default app;
My testing attempts
import { expect } from "chai";
import sinon from "sinon";
import request from "supertest";
import app from "./app.js";
import { controller } from "./controller.js";
describe("my controller", function () {
describe("GET", function () {
it("tries to get a post", async function () {
// attempt 1 -- times out
const controllerStub = sinon.stub(controller, "get").resolves({..json});
await request(controllerStub)
.get("/named-route")
.then(async (res) => {
sinon.assert.match(res.body.statusCode, 200);
});
// attempt 2 -- times out
const controllerStub = sinon.stub(controller, "get").callsFake(
async () => new Promise((resolve) => {
resolve({statusCode: 200})
});
);
// same test call as above
// works but hits actual DB, problematic for POST type requests
const response = await request(app)
.get("/named-route");
expect(response.statusCode).to.equal(200);
});
});
});
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 decided to rewrite my application from spa vue to nuxt but i faced a problem in spa, in vue i could just import store and used it but in nuxt i cant do the same, i am getting undefined object every time
in vue spa i had folder with some api services and with main api.js which include this code
import axios from 'axios'
import store from '#/store/index'
export default () => {
return axios.create({
baseURL: `http://myip.com/`,
headers: {
Authorization: `Bearer ${store.state.token}`
}
})
}
then in other js files i just import this api.js file and use something like this
import Api from '#/services/Api'
export default {
register (credetinals){
return Api().post('register', credetinals)
},
login (credetinals){
return Api().post('login', credetinals)
}
}
so i had 10 js files services where i include api.js and then in files *.vue i just import this service
how can i do the same in nuxt ?
If you add your state under the /store/index.js it will automatically append in the application, you dont need to import it in your file.
What you can do is try to check the state using:
window.$nuxt.$store.state
Also dont forget to add a condition process.browser since you want to check it in browser.
Here is what I did in my api.js file for your reference:
// Libraries
import axios from 'axios';
// API Instance
export const instance = axios.create({
baseURL: 'http://myip.com/',
headers: {},
validateStatus: status => {
return status;
},
});
// API Interceptors
instance.interceptors.request.use(config => {
if (process.browser) {
const userToken = window.$nuxt.$store.getters.getUserToken;
// Set Authorization token
if (userToken) {
config.headers['Authorization'] = `Bearer ${userToken}`;
}
}
return config;
}, function (error) {
return Promise.reject(error);
});
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 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";