How do you get query params from the url in getInitialProps? - javascript

I have a clean url that contains some query param like this.
http://localhost:3000/post/:id
I'm trying to capture the query parameter 'id' on the client side like this.
static async getInitialProps({req, query: { id }}) {
return {
postId: id
}
}
render() {
const props = {
data: {
'id': this.props.postId // this query param is undefined
}
}
return (
<Custom {...props}>A component</Custom>
)
}
My express endpoint looks like this.
app.post(
'/post/:id',
(req, res, next) => {
let data = req.body;
console.log(data);
res.send('Ok');
}
);
But my server side console output ends up like this.
{ id: 'undefined' }
I've read the docs and the github issues but I can't seem to understand why this is happening.

Your frontend code is correct, fetching the post id from the query string is the way to go.
However your backend code is incorrect, first you need to use a GET route to render a Next.js page, and you must extract the path params to craft the final query params as a combination from both the regular query params as well as those path params, this could look like this using express:
const app = next({ dev: process.env.NODE_ENV === 'development' });
app.prepare().then(() => {
const server = express();
server.get('/post/:id', (req, res) => {
const queryParams = Object.assign({}, req.params, req.query);
// assuming /pages/posts is where your frontend code lives
app.render(req, res, '/posts', queryParams);
});
});
Check this Next.js example: https://github.com/zeit/next.js/tree/canary/examples/parameterized-routing for more info.

Related

How do I query with url parameter?

I am new to Node.js and trying to check if an e-mail is already taken by sending the email as a url parameter from iOS app. It is not working, not sure what I am doing wrong.
I am unable to console.log the email parameter in VSCode sent from the front-end, it DOES print in XCODE ( http://localhost:3000/api/user/email/test#gmail.com ) and I know the backend is getting the GET request.
My router code is:
const express = require(`express`)
const router = new express.Router()
const User = require(`../models/user-model`) // import User model
router.get(`/api/user/email/:email`, async (req, res) => {
console.log(req.params) // does NOT print email: test#gmail.com
try {
const user = await User.findOne(req.params.email)
if (user) {
console.log(user._id)
res.send({ available: false })
} else {
res.send({available: true})
}
} catch {
res.status(404).send()
}
})
Thank you!
const express = require(`express`)
const app = new express();
app.get(`/api/user/email/:email`, async (req, res) => {
console.log(req.params) // does NOT print email: test#gmail.com
try {
// const user = await User.findOne(req.params.email)
const user = {_id:123};
if (user) {
console.log(user._id)
res.send({ available: false })
} else {
res.send({available: true})
}
} catch {
res.status(404).send()
}
})
app.listen(3000,function(){
console.log("running");
})
Editing this.. I dont have enough points to comment.. your route seems to be fine, maybe you are not telling your application to use this route, somewhere before starting your application you should have something like:
this.app = new express();
...
this.app.use('/api', MailRouter); //<=== Adding your required mail route
...
I use to split url one parte here (/api) and the other one in the router (/user/email/:email). I'm not sure how to do it by adding it fully to the router (Maybe '/' maybe '')

Node/Express - use API JSON response to (server-side) render the app

Preamble: I'm new to web dev so maybe this might be a very basic question for you vets.
I'm using MVC architecture pattern for this basic app. I've models (MongoDB), views (Express Handlebars), and controllers (functions that take in req, res, next and returns promises (.then > JSON is returned, .catch > error is returned). I'll be routing the paths reqs to their corresponding api endpoints in the controllers.
This makes sense (right?) when I'm purely working on API calls where JSON is the res. However, I also want to call these api endpoints > get their res.json > and use that to render my HTML using Handlebars. What is the best way to accomplish this? I can create same controllers and instead of resp being JSON, I can do render ("html view", res.json). But that seems like I'm repeating same code again just to change what to do with the response (return JSON or Render the JSON).
Hope I'm making sense, if not, do let me know. Please advise.
p.s. try to ELI5 things for me. (:
Edit:
//Model Example
const Schema = require('mongoose').Schema;
const testSchema = new Schema({
testText: { type: String, required: true },
});
const Test = mongoose.model('Test', testSchema);
module.exports = Test;
//Controller Example
const model = require('../models');
module.exports = {
getAll: function(req, res, next) {
model.Test.find(req.query)
.then((testItems) => {
!testItems.length
? res.status(404).json({ message: 'No Test Item Found' })
: res.status(200).json(testItems);
})
.catch((err) => next(err));
},
};
//Route Example
const router = require('express').Router(),
controller = require('../controllers');
router.get('/', controller.getAll);
module.exports = router;
I want the endpoints to return JSON and somehow manage whether to render (if the req comes from a browser) or stay with JSON (if called from Postman or an API web URL for example) without repeating the code. I'm trying to not create two endpoitns with 99% of the code being the same, the only difference being .then > res.status(200).json(testItems); vs .then > res.status(200).render('testPage', { testItems}).
For postman you could check the existence of postman-token in req.headers, then you could render accordingly, something like this:
req.headers['postman-token'] ? res.json({ /* json */ }) : render('view', {/ * json */});
If you want to go with checking postman token then you can use similar to method1.
if you want to check with query params in this case you can get json response or html even from browser for future use also and is not dependent on postman then use similar to method2 of the following example.
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
const port = 5000
app.get('/method1', (req, res) => {
const isJSONResp = req.headers['postman-token']
const resp = { status: "hello" }
if (isJSONResp) {
res.json(resp)
} else {
res.render('some.html', resp)
}
})
app.get('/method2', (req, res) => {
const params = req.params
const resp = { status: "hello" }
if (params.resp === 'json') {
res.json(resp)
} else {
res.render('some.html', resp)
}
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

ApolloServer 2.0 context and public/private parts of the GraphQL API

I'm not a pro in any way but I've started and ApolloServer/Express backend to host a site where I will have public parts and private parts for members. I am generating at JWT token in the login mutation and get's it delivered to the client.
With context I want to check if the token is set or not and based on this handle what GraphQL queries are allowed. My Express/Apollo server looks like this at the moment.
const server = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
// get the user token from the headers
const token = (await req.headers.authorization) || '';
if (token) {
member = await getMember(token);
}
}
});
The problem is that this locks down the GraphQL API from any queries and I want/need to reach signup/login mutations for example.
Could anyone spread some light on this to help me understand what I need to do to get this to work.
the way i am doing it is that i will construct auth middleware even before graphql server as sometimes is needed to have information about authenticated user also in other middlewares not just GraphQL schema. Will add some codes, that you need to get it done
const auth = (req, res, next) => {
if (typeof req.headers.authorization !== 'string') {
return next();
}
const header = req.headers.authorization;
const token = header.replace('Bearer ', '');
try {
const jwtData = jwt.verify(token, JWT_SECRET);
if (jwtData && jwtData.user) {
req.user = jwtData.user;
} else {
console.log('Token was not authorized');
}
} catch (err) {
console.log('Invalid token');
}
return next();
};
This way i am injecting the user into each request if the right token is set. Then in apollo server 2 you can do it as follows.
const initGraphQLserver = () => {
const graphQLConfig = {
context: ({ req, res }) => ({
user: req.user,
}),
rootValue: {},
schema,
};
const apolloServer = new ApolloServer(graphQLConfig);
return apolloServer;
};
This function will initiate ApolloServer and you will apply this middleware in the right place. We need to have auth middleware before applyin apollo server 2
app.use(auth);
initGraphQLserver().applyMiddleware({ app });
assuming the app is
const app = express();
Now you will have user from user jwtData injected into context for each resolver as "user", or in req.user in other middlewares and you can use it for example like this. This is me query for saying which user is authenticated or not
me: {
type: User,
resolve: async (source, args, ctx) => {
const id = get(ctx, 'user.id');
if (!id) return null;
const oneUser = await getOneUser({}, { id, isActive: true });
return oneUser;
},
},
I hope that everything make sense even with fractionized code. Feel free to ask any more questions. There is definitely more complex auth, but this basic example is usually enough for simple app.
Best David

Dynamic path in express routes

I wonder how to using path in express dynamically. For example, i'm using lodash for finding a path in different file with regex method.
routes.js
const json = require('./routes.json')
const _ = require('lodash')
routes.use(function(req, res, next) {
let str = req.path
let path = str.split('/')[1]
// [Request] => /test/123
console.log(path)
// [Result] => test
let test = _.find(json.routes, function(item) {
return item.path.match(new RegExp('^/' + path + '*'))
})
console.log(test)
//{"path" : "/test/:id", "target" : "localhost:2018", "message" : "This is Test Response" },
routes.get(test.path, function(req, res) {
res.json("Done")
})
})
On above code, i just nested the routes. But there's nothing any response. Is there any ways to do this? This method also i want to use with DB if necessary. Thanks anyway
Using middleware is impossible. When a request comes, expressjs will search a registered path first.
So here we go why that code not running as well.
For example, I'm as an user request : localhost:2018/test/123
Please following my comment in below
const json = require('./routes.json')
const _ = require('lodash')
routes.use(function(req, res, next) {
let str = req.path
let path = str.split('/')[1]
// [Request] => /test/123
console.log(path)
// [Result] => test
let test = _.find(json.routes, function(item) {
return item.path.match(new RegExp('^/' + path + '*'))
})
console.log(test)
//{"path" : "/test/:id", "target" : "localhost:2018", "message" : "This is Test Response" },
//And now, the routes has been registered by /test/:id.
//But, you never get response because you was hitting the first request and you need a second request for see if that works. But you can't do a second request, this method will reseting again. Correctmeifimwrong
routes.get(test.path, function(req, res) {
res.json("Done")
})
})
How to approach this goal then? However, we need a registering our routes inside app.use or routes.use . So far what i got, we can using loop in here.
//Now, we registering our path into routes.use
_.find(json.routes, function(item) {
routes.use(item.path, function(req, res) {
res.json("Done")
})
})
//The result become
/**
* routes.use('/test:id/', function(req, res, next){
res.json("Done")
})
routes.use('/hi/', function(req, res, next){
res.json("Done")
})
*/
Reference : Building a service API Part 4
Thanks anyway, leave me a comment if there's something wrong with this method :D

Using findOne and findOneAndUpdate with HTTP request (mongoose)

I am making an api rest in which I want to make HTTP requests using Postman, specifically I want to perform a search or update a mongodb document, but this must be by an id which is not the doc_id that provides mongo
models Schema
'use strict'
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const infoClientSchema = Schema ({
idusr: String, /*this is require*/
name: String,
phone: Number,
address: String,
riff: String,
state: String,
city: String,
email: {type: String}
})
module.exports = mongoose.model('InfoCli',infoClientSchema)
Controller (This is the get method I know using findById and is working)
'use strict'
const InfoCli = require('../models/infoclient')
function getInfoCli(req, res){
let infocliId = req.params.infocliId
InfoCli.findById(infocliId, (err, infocli) =>{
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!infocli) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infoclient: infocli})
})
}
Controller (This is the get method which I thought would work using findOne)
function getInfoByUsr(req, res){
let idusr = req.body.idusr
InfoCli.findOne(idusr, (err, infocli) => {
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!infocli) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infoclient: infocli})
console.log(infocli) /*The console is not showing anything*/
})
}
Controller (This is the put method which I thought would work using findOneAndUpdate)
function updateByUsr(req, res){
let idusr = req.body.idusr
let update = req.body
InfoCli.findOneAndUpdate(idusr, update, (err, infocliUpdate) => {
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!idusr) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infocliente: infocliUpdate})
})
}
Routes (not 100% sure about this)
const express = require('express')
const InfoCliCtrl = require('../controllers/infoclient')
const api = express.Router()
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli) /*working*/
api.get('/infoclient/:idusr', InfoCliCtrl.getInfoByUsr)
In your app.js/server.js
you should have bodyparser installed
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli)
api.post('/infoclient/:idusr', InfoCliCtrl.updateByUsr)
If you are passing data as URL parameter, like this /infoclient/:infocliId then you can access that using req.params.infocliId
If you are passing using POST body then you can access data using req.body.
In infoClient.js
To fetch user data
exports.getInfoCli = function(req, res, next){
var incomingData = req.params.infocliId;
InfoCli.findOne({idusr: incomingData}, function(err, data){
if(err){
return res.status(500);
} else {
return res.status(200).send({infoclient: data})
}
});
}
Call the above code by
GET - http://localhost:port/infoclient/3874234634 this 3874234634 is your infocliId you need to pass in route
To update user data
exports.updateByUsr = function(req, res, next){
var userId = req.params.idusr;
var updateData = req.body;
InfoCli.findOneAndUpdate({idusr: userId}, updateData, {new: true }, function(err, data){
if(err){
return res.status(500);
} else {
return res.status(200).send(data)
}
});
}
In the update code we have used {new : true} is to return updated document from DB
Call the above code by
POST method - http://localhost:port/infoclient/3874234634 with data in POST body {name: 'pikachu', phone: 12345, ...}
so you read the userid in url parameter using req.params and body data in req.body
I think you simply need to change the line let idusr = req.body.idusr in your getInfoByUsr() function to let idusr = req.params.idusr
http://expressjs.com/en/api.html#req.body
http://expressjs.com/en/api.html#req.params
Also check the syntax of your findOne and findOneAndUpdate query (because idusr is not a Mongo _id but sort of custom String id):
InfoCli.findOne({ idusr: idusr }, (err, infocli) => { ...
InfoCli.findOneAndUpdate({ idusr: idusr }, update, (err, infocliUpdate) => {..
http://mongoosejs.com/docs/api.html#model_Model.findOne
Thank you all, your answers help me to correct many things in the code.
The problem was a horrible mistake in the routes
See how I was using the same path for two requests
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli) /*working*/
api.get('/infoclient/:idusr', InfoCliCtrl.getInfoByUsr)
The problem was that when I used the identifier for idusr it was in conflict with the ObjectId search
Now
api.get('/infoclient/idusr/:idusr', InfoCliCtrl.getInfoByUsr)

Categories

Resources