how to make required and optional date params in express? - node.js

I am having difficulty making a route like this as I am new in express routing:
GET users's exercise log: GET
/api/exercise/log?{userId}[&from][&to][&limit]
{ } = required, [ ] = optional
from, to = dates (yyyy-mm-dd); limit = number
I don't understand what's that ? and & on the route for? what i see on youtube are routes like this:
app.route('/api/exercise/log/:userId').post(exercisehandler);
I want to make a route like this:
GET /api/exercise/log?userId=1234&from=2018-01-01&to=2018-07-23&limit=100
I am looking on the routing doc on express but I still want to get an idea here so i can get this done quicker. help?

You could do it this way:
const express = require("express")
const app = express()
const port = 3000
app.route("/api/exercise/log/:userId").get((req, res) => {
console.log(req.params)
console.log(req.query)
// ...
})
app.listen(port, () => console.log(`Listening on port ${port}!`))
and the GET request:
GET /api/exercise/log/1234?from=2018-01-01&to=2018-07-23&limit=100

Related

Retrieve particular values from the POST endpoint in Express

I have an API definition /task/{activityId}?status={status} ( method = POST)
Input -> activityId, status
Output -> status
In Express I have written my code like this for debugging purpose -
const express = require("express");
const app = express();
const cors = require("cors");
const pool = require("./db");
const axios = require('axios');
app.use(cors());
app.use(express.json());
app.post("/task/:activityId?status=:status", async (req, res) => {
try {
var activityId = req.params.activityId;
var status = req.params.status;
console.log(status);
console.log(activityId);
if (status == "COMPLETE")
const updateStatus = await pool.query("update public.\"TableOne\" set \"Status\"='COMPLETE' where \"ActivityId\"='" + activityId + "'");
}
catch (err) {
console.error(err.message);
}
})
app.listen(5000, () => {
console.log("server has started on port 5000");
})
I am not able to see the values in console of activity id and status passed when I am hitting the endpoint from postman with something like this -
[POST] http://hostname:port/task/A1?status=PENDING
What mistake am I making here?
In order to get values from parameter, proper way is like this
console.log(req.params.status);
But secondary parameter named status is stated as querystring parameter, So, you need to fetch like this,
console.log(req.query.status);
Also, you don’t need to mention status in the code, so, your code to fetch the param should be like this:
app.post("/task/:activityId", async (req, res) => {
As you can see, I didn’t mention the status parameter. Still I will get it.

How to attach a router to an express app based on a path specified by a regular expression?

With this very simple app:
const express = require('express');
const app = express();
const router = express.Router();
const port = 8080;
router.get('/test', (req, res) => {
res.send('Test was hit')
})
// binds router to express app
app.use('/root', router);
app.listen(port, () => logger.info(`Listening on port: ${port}`));
After running curl http://localhost:8080/root/test, unsurprisingly, the response is Test was hit
I am to make this much more generic and wish for the consumer to be able to hit curl http://localhost:8080/<whatever-the-consumer-specifies>/test and still hit the routers /test endpoint.
However, if I replace the binding to be as follows:
app.use('/*',router);
After a subsequent hit the response is Cannot GET /root/test
How can I instead achieve this?
* EDIT: *
Of course I could do:
router.get('*/test', (req, res) => {
res.send('Test was hit')
})
However - This is not the answer that I seek, I only want this configuration take place once in the project.
Express allows the use of regular expression in the router middleware as mentioned in the docs here.
app.use(/\/\w+/, router);
You could replace the string and place a regex in the first argument.

Express - get value of path matching string

Question
I've got several nested routers, and would like to get access to the whole string that the request's path matched. It's a little hard to say with english, so take a look at this code:
const express = require('express')
const app = express()
const router1 = express.Router()
const router2 = express.Router()
// set up router 2 paths
router2.get('/path2/:param2', (req, res, next) => {
const someVar = req.something // the value I'll talk about in a second
return res.status(200).send({ someVar })
})
// set up router1 paths
router1.use('/path1/:param1', router2)
// connect the routers behind a base url
app.use('/api/v1', router1)
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
If I were to make a GET request with:
curl -X GET http://localhost:3000/api/v1/path1/myparam1/path2/myparam2
I want that to return an object like this:
{
"someVar": "/api/v1/path1/:param1/path2/:param2"
}
Context
I have middleware in my app which logs the path to an elasticsearch cluster, and I'd like the cluster to group paths by the string they used to match the request, rather than the request itself. That way I can get a visual of which request endpoints are being hit the most.
// you need to set mergeParams: true on the router,
// if you want to access params from the parent router
var router1 = express.Router({mergeParams: true});

swagger-ui-express Multiple Routes for Different API Documentation

I have 2 separate swagger API documentations which I want to run via swagger-ui-express NPM package, and my express server is starting fine on port 5000, but when I am trying to access any of the URL always getting the 404 error, Here is my app.js file and URL's for your reference:
Route 1: http://localhost:5000/edi
Route 2: http://localhost:5000/ecom
const express = require('express');
const router = require('express').Router();
const swaggerUi = require('swagger-ui-express');
const ediSwaggerDocument = require('./edi-openapi.json');
const ecomSwaggerDocument = require('./ecom-openapi.json');
const SWAGGER_APP_PORT = process.env.SWAGGER_APP_PORT || 5000;
const app = express();
// Route Middleware to be called before serving Any Route
router.use('/', swaggerUi.serve);
// Route - EDI RESTful API Documentaion
router.get('/edi', swaggerUi.setup(ediSwaggerDocument));
// Route - eCommerce RESTful API Documentaion
router.get('/ecom', swaggerUi.setup(ecomSwaggerDocument));
app.listen(SWAGGER_APP_PORT, () => console.log(`RESTful API Up and Running on Port ${SWAGGER_APP_PORT}`));
Try the following configurations to hook swaggerUi with express-app
app.use("/edi", swaggerUi.serve, (...args) => swaggerUi.setup(ediSwaggerDocument)(...args));
app.use("/ecom", swaggerUi.serve, (...args) => swaggerUi.setup(ecomSwaggerDocument)(...args));
I haven't dig enough into the swagger-ui-express but i think the problem comes from the function generateHTML (called in swaggerUi.setup). A global module variable (swaggerInit) is updated when called.
So the last call to generateHTML has side effects on every routes that use swaggerUi.setup middleware.
A quick fix is to generate HTML each time the route is called. According to the code snippet you provide, it should looks like :
let swaggerDocEdi = require('./edi-openapi.json');
let swaggerDocEcom= require('./ecom-openapi.json');
let router = express.Router();
router.use('/api/edi', swagger.serve, (req, res) => {
let html = swagger.generateHTML(swaggerDocEdi);
res.send(html);
});
router.use('/api/ecom', swagger.serve, (req, res) => {
let html = swagger.generateHTML(swaggerDocEcom);
res.send(html);
});
Note that the global variable is still updated.
router.use('/jobs/api/:id',swaggerUi.serve,(req,res)=>{
console.log("here")
let a = req.params.id
if(a==='all'){ res.status(200).send(swaggerUi.generateHTML(swaggerDocument))}
if(a==='google'){res.status(200).send(swaggerUi.generateHTML(GoogleAds)) }
});
I was also looking for a solution for this issue, and found a better solution recommended by Swagger UI Express. check the link below
https://github.com/scottie1984/swagger-ui-express#two-swagger-documents
const express = require('express');
const app = express();
const swaggerUi = require('swagger-ui-express');
const swaggerDocumentOne = require('./swagger-one.json');
const swaggerDocumentTwo = require('./swagger-two.json');
var options = {}
app.use('/api-docs-one', swaggerUi.serveFiles(swaggerDocumentOne, options), swaggerUi.setup(swaggerDocumentOne));
app.use('/api-docs-two', swaggerUi.serveFiles(swaggerDocumentTwo, options), swaggerUi.setup(swaggerDocumentTwo));
Hope this will help others as well.
It looks like the Router is being used incorrectly. For this simple use case I would recommend adding your routes directly to the app instance. See:
const express = require('express');
// xxxx const router = require('express').Router();
const swaggerUi = require('swagger-ui-express');
const ediSwaggerDocument = require('./edi-openapi.json');
const ecomSwaggerDocument = require('./ecom-openapi.json');
const SWAGGER_APP_PORT = process.env.SWAGGER_APP_PORT || 5000;
const app = express();
// Route Middleware to be called before serving Any Route
app.use('/', swaggerUi.serve); // replaced router with app
// Route - EDI RESTful API Documentaion
// REPLACED "router" with "app"
app.get('/edi', swaggerUi.setup(ediSwaggerDocument));
// Route - eCommerce RESTful API Documentaion
// REPLACED "router" with "app"
app.get('/ecom', swaggerUi.setup(ecomSwaggerDocument));
app.listen(SWAGGER_APP_PORT, () => console.log(`RESTful API Up and Running on Port ${SWAGGER_APP_PORT}`));
Now, you could use the Router by adding it to the app instance with app.use(). See:
// Route - EDI
router.get('/edi', swaggerUi.setup(ediSwaggerDocument));
// Route - eCommerce
router.get('/ecom', swaggerUi.setup(ecomSwaggerDocument));
// Adding it to App instance
app.use('/swagger', router)
// End Point => localhost:5000/swagger/edi
Hope this helps!!

How to save external API response to Firebase

Im working with a React App where I present a list top Podcasts. I'm using iTunes Search API to dynamically present data to the user. For now, I working with a Node Express server to setup my custom endpoints. The problem is that the API has a request limit, so I tought that I could save what I get from the response to Firebase and present the data from firebase instead.
To my question;
Can in some way save the response I get from iTunes Search API to Firebase?
For now my code for fetching data from my API Endpoints looks like this in my Node+Express server:
const express = require('express');
const unirest = require('unirest');
const app = express();
const port = process.env.PORT || 5000;
// Get all Episodes from a specific podcast
app.get('/api/podcast/episodes', (req, res) => {
const feedurl = req.query.feedurl
unirest.get(feedurl)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast by ID
app.get('/api/podcast/:id', (req, res) => {
const podID = req.params.id;
unirest.get(`https://itunes.apple.com/lookup?id=${podID}&country=se`)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast Categorys
app.get('/api/podcast/:category/:amount', (req, res) => {
const categoryID = req.params.category;
const amount = req.params.amount;
unirest.get(`https://itunes.apple.com/se/rss/toppodcasts/limit=${amount}/genre=${categoryID}/explicit=true/json`)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast Categorys
app.get('/api/categorys', (req, res) => {
unirest.get('https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres?id=26&cc=se')
.end((response) => {
res.status(200).send(response.body)
});
});
app.listen(port, () => console.log(`Listening on port ${port}`));
Im just looking for someone who could point me in the right direction how to proceed. Cause for now I'm stuck, big time.
Depending on how long you want to cache the response, you can use a whole different things - a physical database like MySql, Sqlite, MongoDB etc to locally persist data.
If you only want to keep the cached result for a short period of time, you can use in-memory cache or just any other tool that offers you same functionality. Redis is also a good contender as a temporary store, especially when you expect to scale to more than one node instance for your application.
Below, I have modified a part of your code to cache result for 10mins, using memory-cache npm module
const express = require('express');
const unirest = require('unirest');
const cache = require('memory-cache');
const CACHE_DURATION = 10 * 60 * 1000; //10mins
const app = express();
const port = process.env.PORT || 5000;
// Get all Episodes from a specific podcast
app.get('/api/podcast/episodes', (req, res) => {
const cacheKey = req.query.feedurl; //Or anything unique to this route
const cachedData = cache.get(cacheKey);
if(cachedData) {
return res.json(cachedData);
}
const feedurl = req.query.feedurl
unirest.get(feedurl)
.end((response) => {
res.status(200).send(response.body);
cache.put(cacheKey, response.body, CACHE_DURATION);
});
});
---- the rest of your code ----
You can hit the route as many times as you want and be guaranteed that data will be fetched from iTunes only once in 10mins.
The second and subsequent requests will be served a lot faster from cache.
Let me know if this is what you are looking for.

Resources