How should I rewrite this as a function?
I'm building a tool using Next.JS & using their api endpoint to interact with MongoDB.
The NextJS website says:
Note: You should not use fetch() to call an API route in your application. Instead, directly import the API route and call its function yourself. You may need to slightly refactor your code for this approach.
Fetching from an external API is fine!
How should I refactor my code to adjust to this approach?
import handler from '../../middleware/common_middleware';
handler.get(async (req, res) => {
try {
let query = req.query;
let queryName = Object.values(query)[0];
let doc = await req.db.collection("Volunteers").find({"_id": queryName}).toArray();
res.status(201).json(doc);
}
catch {
res.status(400).send("The profile doesn't exist.");
}
});
export default handler;
Looking at NextJS doc here:
https://nextjs.org/docs/api-routes/introduction
https://github.com/vercel/next.js/blob/canary/examples/api-routes-rest/pages/api/users.js
you probably should have a structure like this:
export default async (req, res) => {
try {
let query = req.query;
let queryName = Object.values(query)[0];
// modify with the module to make query to db
let doc = await req.db.collection("Volunteers").find({"_id": queryName}).toArray();
res.statusCode = 201;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({status:'success', data: doc});
}
catch {
res.statusCode = 400
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({status: 'error', data: 'The profile doesn't exist.' }))
}
}
Didn't try it but hope it gives an idea.
Related
I'm trying to simply upload a single file from the client (react/axios) to the server (multer / express). I've read through every "req.file undefined" and can't seem to see the same issues with my own code.
The other issue is that actually my req on the server sees the file in the "files", but multer doesn't save it and req.file is undefined.
What could be happening here?
For client I've tried both methods of sending the form data, neither work.
const onAnalyze = async () => {
if (selectedFile !== null) {
//we have a file, so that's what we're sending
var formData = new FormData();
formData.append("analyze", selectedFile);
//let res = await api.post('/analyze/upload', formData)
try {
const response = await axios({
method: "post",
url: "http://localhost:5000/analyze/upload",
data: formData,
header: { "Content-Type": "multipart/form-data" }
});
console.log(response)
} catch (error) {
console.log(error)
}
// console.log(res)
// setAnalysis(res.data)
} else if (text.length <= maxLength) {
let res = await api.post('/analyze', { text: text })
setAnalysis(res.data)
}
}
For the server it seems simple.. I just don't know. This file destination exists. req.file is always undefined
import express from 'express';
import { getMedia, createMedia } from '../controllers/media.js';
import { AnalyzeText, AnalyzeFile } from '../controllers/analyze.js'
import multer from 'multer'
const fileStorageEngine = multer.diskStorage({
destination: "uploads",
filename: (req, file, cb) => {
cb(null, file.originalname)
}
});
var upload = multer({ storage: fileStorageEngine })
const router = express.Router();
//Get All Movies and TV shows.
router.get('/', getMedia);
//Request to create a new item based on a title
router.post('/', createMedia);
//Recuist to analyze information (not sure if this should be a post or not)
router.post('/analyze', AnalyzeText)
router.post('/analyze/upload', upload.single('analyze'), (req, res) => {
console.log(req.file)
res.status(200).json('well we found it again');
});
Turns out I had another middleware running that was wrapping my file upload. Removed that, everything works.
If you're using react you may face this problem sending your request with axios. But I solved it by adding a name attribute to my input element. And removing the new formData method totally and passing the input.file[0] into axios, content-type multipart-formdata, and you must use the multer.diskStorage method. If not your image would be saved as text file
I want to make a VSC extension that involves posting to my API, however when I write my fetch syntax out to POST to my server, it doesn't work. So I thought maybe I need to add node-fetch, so I did
npm i --save node-fetch
and it says This expression is not callable. and once again, it still can't make the POST request.
I have used axios to post to a URL:
import * as FormData from 'form-data';
import axios from 'axios';
const form = new FormData();
form.append('srcmbr', save_folderContent.srcmbr);
form.append('srcfName', save_folderContent.srcfName);
form.append('srcfLib', save_folderContent.srcfLib);
const headers = form.getHeaders();
headers['Content-length'] = await form_getLength(form);
{
const result = await axios.post(
`${serverUrl}/site/common/rmvm-srcmbr.php`, form,
{ headers, });
console.log(`delete-srcmbr ${result.data}`);
}
export function form_getLength(form: FormData)
{
return new Promise((resolve, reject) =>
{
form.getLength((err, length) =>
{
resolve(length);
});
});
}
With node-fetch you can do something like this:
const fetch = require('node-fetch');
async function main () {
const myUrl = 'https://api.example.com/route';
const myData = {};
const response = await fetch(myUrl, {
method: 'POST',
body: JSON.stringify(myData),
}).then((response) => response.json());
console.log(response);
}
main();
I forgot to add .default at the end of the axios require.
so it would be
const axios = require('axios').default;
IF YOU'RE USING TYPESCRIPT PLEASE REFER TO #RockBoro 's POST!!!
I am using an Angular front-end with a Nodejs backend. Im currently proxying all my front-end requests through my express server. However when I make my http request to the Here API I am rejected due to an invalid combination of app_id and app_code.
angular service
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http'
import { HttpParams } from '#angular/common/http'
#Injectable({
providedIn: 'root'
})
export class GetReqPlaces {
constructor(private http: HttpClient) { }
getPlaces(wLong,sLat,eLong,nLat){
// let obj = {params: {westLong: wLong, southLat: sLat, eastLong:eLong, northLat:nLat }};
let params = new HttpParams().set("westLong" , '-97.783').set("southLat", '30.231').set( "eastLong" , '-97.740').set("northLat", '30.329');
return this.http.get( 'api/find/places', { params : params}).subscribe(res=>console.log(res))
}
}
server.js
const express = require("express")
const bodyParser = require("body-parser")
const cors = require("cors")
const path = require("path")
const app = express();
const request = require("request")
const environment= require('./keys')
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
let reqPath = __dirname.substring(0,__dirname.length-7)
app.use(express.static(path.join(reqPath, '/dist/angular-places-search')));
app.get('/api/find/places', (req, res) => {
let appId = environment.environment.appId;
let appCode = environment.environment.appCode;
let URL= `https://places.cit.api.here.com/places/v1/discover/search?app_id={${appId}}&app_code={${appCode}}&in=${req.query.westLong},${req.query.southLat},${req.query.eastLong},${req.query.northLat}&pretty`;
console.log(URL)
request(URL, function (error, response, body) {
let data={
body:body,
};
console.log(error,response)
res.send(data);
});
});
app.get('/test', (req, res) => res.send('Well this route was a hit! Bada....tsss'));
// CATCH ALL
app.get('*', (req, res) => {
res.sendFile(path.join(reqPath, 'dist/angular-places-search/index.html'));
});
app.listen(4000, () => console.log(`Express server running on port 4000`));
Before this I was running into CORS and request issues but I think I sorted those out. Based on my research on this same error code (In the context of the framework that Im working in), people overwhelmingly suggest to wait for tokens to register with Here API. Waiting two days is enough I think, still doesnt work. Then there is the very popular solution of just scratching the Here freemium and starting a new project, which I did, and which did not solve my issue. Very few things I have 100% certainty on but I did copy my keys correctly and the URL path built is according to the required Here syntax.
If anyone has any insight you will be my Hero, and also the catalyst for my continued learning :D. Happy Sunday!
In addition the incoming message I get through express is :
method: 'GET',
path: '/places/v1/discover/search?app_id=%notmyid%7D&app_code=%normycode%7D&in=-97.783,30.231,-97.740,30.329&pretty'
However i dont know why it is setting the app_id=% instead of using {}, when i console log the URL it is correct, with my app_id and app_code
The %7D is the url encoded value of the symbol } (urlencoding) which is done by most libraries. For using the HERE API you should not enclose the app_id/app_code between {}. They should be provided directly as strings, check the examples
I have been fiddling with this for days, and I cannot figure out why the Mongoose middleware is not being invoked.
So I have an API in node.js and I have a website using Angular.js. The Mongoose middleware is this:
schema.post('remove', function (doc) {
console.log('doctors - post - remove');
});
So this hook is called perfectly fine when invoked from the Angular front end. However, when I run a test with supertest, chai, and mocha the hook is not invoked. Here is my code for the testing:
it('/doctors - POST - (create doctor)', function(done){
request(app)
.post('/doctors')
.send(doctor)
.end(function (err, res){
if (res.body['error']) {
expect(S(res.body['error']).startsWith('doctor already exists')).to.be.true;
}
else
expect(res.body['email']).to.equal(doctor['email']);
done();
});
});
....
it('/doctors/remove - DELETE', function(done){
request(app)
.del('/doctors/remove')
.auth(new_doctor_creds["email"], new_doctor_creds["pass"])
.end(function (err, res){
expect(Object.keys(res.body).length).to.not.equal(0);
done();
});
});
And here is my route for the express app:
app.delete('/doctors/remove', authController.isAuthenticated, function (req, res, next) {
var email = req.user['email'];
Doctors.findOne({email:email}).remove(function (err, removed) {
if (err) return next(err);
return res.status(200).send(removed);
});
});
Again, this Mongoose middleware works perfectly fine when invoked from an API call from the Angular app. However, it does not work when tested with supertest. Any ideas on what to do here?
EDIT: I tried to recreate this example with a simplified version that way you can see all of the code. So here is a two file version that is STILL not working. Here is the app.js:
var mongoose = require('mongoose');
var app = require('express')();
var http = require('http');
var fs = require('fs');
var Doctors = require('./schema');
mongoose.connect('mongodb://localhost/m4', function(err) {
if (err) throw err;
console.log('connected');
app.get('/post', function (req, res, next) {
console.log('create');
Doctors.create({email:"hello"}, function (err, inserted) {
if (err) console.log(err);
res.end();
});
});
app.get('/delete', function (req, res, next) {
console.log('removed');
Doctors.remove({email:"hello"}, function (err, removed) {
if (err) console.log(err);
res.end();
});
});
http.createServer(app).listen('6000', function () {
console.log('now listen on localhost:6000');
});
});
and the schema:
var mongoose = require('mongoose');
var schema = mongoose.Schema({
email: { type: String }
});
schema.pre('save', function (next) {
console.log('doctors - post - save');
next();
});
schema.post('remove', function (doc) {
console.log('doctors - post - remove');
});
module.exports = mongoose.model('Doctors', schema);
Here's what I suggest. Let's perform the #remove on the doc found by #findOne. If I remember correctly, remove post hooks only works on Doc#remove and not on Model#remove.
schema.post('remove', function (doc) {
console.log('doctors - post - remove'); // <-- now runs
});
app.delete('/doctors/remove', authController.isAuthenticated, function (req, res, next) {
var email = req.user['email'];
Doctors.findOne({email: email}, function(err, doc) {
if (err) {
return next(err);
}
doc.remove().then(function(removed) {
return res.status(200).send(removed);
}, function(err) {
next(err);
});
});
});
Mongoose post hooks run AFTER the operation is completed, concurrently with operation callbacks. See the comments below:
Doctors.findOne({email:email}).remove(function (err, removed) {
// All this code and the post hook are executed at the same time
if (err) return next(err);
// Here you send the response so supertest#end() will be triggered
// It's not guaranteed that post remove was executed completely at this point
return res.status(200).send(removed);
});
Post hooks were made to run processes independent of the server response. When you run tests, the server shuts down right after the tests are completed, and maybe it had no time enough to finish the post hooks. In the other hand, when you call the API from a client, normally you keep the server running, so the post jobs can be completed.
Now, there comes a problem: how can we test post hooks consistently? I got up this question because I was looking for a solution to that. If you already have an answer, please post here.
I have an API that returns some JSON from mongodb:
// In router.js
var api = require('api')
app.get('/lists', api.lists);
// In api.js
var db = require('db')
exports.lists = function(req, res) {
db.lists.find({}, function(err, lists) {
res.send(lists);
});
};
Is there a way to design the API so that I could also consume it from within my Node.js app? I'm trying to avoid having to duplicate any of the database code outside the API. I basically have a controller that can render the data server-side:
// In controller.js
var api = require('api')
exports.page = function(req, res) {
res.send(api.lists()); // This won't work
};
I found a hacky solution which was to pass a callback function to the api.lists(), but I have a feeling this is the "wrong" way to achieve this:
// In api.js
exports.lists = function(req, res, callback) {
db.lists.find({}, function(err, lists) {
if(callback){
callback(lists);
} else {
res.send(lists);
}
});
};
Any ideas?
I think the problem is that in your current code you are coupling your API to the response object. You can decouple them with something like this:
In router.js instead of using api.lists as the callback, define a function that will call api.lists with a callback that is wired to the response object. In this case api.list DOES NOT need to know about the response object but the function that we are creating does.
// In router.js
var api = require('api');
app.get('/lists', function(req, res) {
api.lists(function(err, lists) {
if(err) {
res.send('error page');
return;
}
res.send(lists);
});
});
In api.js we remove the reference to the response object. Now it will just call whatever callback it received with the appropriate data (err + lists). It's up to the callback to do whatever it pleases with the result.
// In api.js
var db = require('db')
exports.lists = function(callback) {
db.lists.find({}, function(err, lists) {
callback(err, lists);
});
};