Koa 404 when calling from Vue - node.js

I am trying to build an app with Koa and Nuxt. this is what I have:
Define service to retrieve from firestore:
const Firestore = require('#google-cloud/firestore');
const getItems = () => {
const db = new Firestore({
projectId: '*******',
keyFilename: "******"
});
db.collection('items').get()
.then((snapshot) => {
return snapshot;
})
}
Define them in routes.js:
const Router = require('#koa/router');
const articleService = require('./services/itemservice');
const router = new Router();
router.get('/getitems', async(ctx, next) => {
ctx.body = articleService.getItems();
});
module.exports = router;
Add routes to retrieve from routes.js:
app.use(router.routes());
app.use(router.allowedMethods());
And finally call it from a component:
let articles = axios.get('/getitems')
.then(response => {
console.log(response);
})//.....
I am receiving this error:
response:
{ status: 404,
statusText: 'Not Found',
headers:
{ 'content-type': 'text/html; charset=us-ascii',
server: 'Microsoft-HTTPAPI/2.0',
date: 'Fri, 25 Oct 2019 16:08:00 GMT',
connection: 'close',
'content-length': '315' },
config:
{ url: '/getarticles',
method: 'get',
headers: [Object],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined },
request:
ClientRequest {
_header:
'GET /getitems HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.19.0\r\nHost: localhost\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/getitems',
_ended: true,
res: [IncomingMessage],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Writable],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
data:
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">\r\n<HTML><HEAD><TITLE>Not Found</TITLE>\r\n<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>\r\n<BODY><h2>Not Found</h2>\r\n<hr><p>HTTP Error 404. The requested resource is not found.</p>\r\n</BODY></HTML>\r\n' },
isAxiosError: true,
toJSON: [Function] }
Can anyone point me in the right direction?

I've never built an app with Nuxt, but I'll try to help you anyway.
First, I recommend you to read about Promises:
- https://github.com/leonardomso/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md
- https://github.com/leonardomso/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch8.md
(those are two chapters of a good JS book series!)
Second, you can try two things in order to find the bug:
- add a .catch block to your thenables, to check if something went wrong;
- add a dummy route that just logs an 'OK', to make sure the routes are be registered and up to respond.
I hope this will help you!

I had this problem with my nuxt / express.js app:
If you would try to type in your browser yourURL/getitems your nuxt app will try to route you to that page instead of just to show u the data.
First thing to do, how to say, you need to define what url your backend should handle.
You go to your nuxt.config.js and add this line of code:
serverMiddleware: ["~/api/index.js"],
That means you have a folder called api and in that folder you have an index.js file and thats your express.js / koa app.
Now in your index.js where your express.js / koa app is you need to add at the end of the line this peace of code:
module.exports = {
path: "/api",
handler: app
};
If everything works fine your URL should have now a prefix api and you should be able to get the data with localhost:PORT/api/getitems
Now nuxt wont try to route you to your url/api because it knows now that this is your backend
If you could provide me your folder structure of your nuxt app i could help you more.
Here is more information about serverMiddleware
https://nuxtjs.org/api/configuration-servermiddleware
EDIT:
somewhere you have a Folder, lets say ist named server or api
in that Folder there should be a index.js file and your routes, model, Controllers etc.
Lets say you have a Folder called server and in that Server you have index.js that should look something like this
const Koa = require('koa');
const app = new Koa();
Import routes from "./routes.js"
app.use(routes)
//here you define now your backend path
module.exports = {
//you can use any path you want
path: "/backend",
handler: app
};
app.listen(3000);
Now you Need to go to your nuxt.config.js file and Point to that index.js File
serverMiddleware: ["~/server/index.js"]
Now you can Access your data with axios:
axios.get("/backend/getitems").then(data => { console.log(data) })
You will Need to add backend to your axios url because thats the path you defined that your Server will handle.

Related

Astro: How to proxy service calls

I am setting up an Astro site which will display data fetched from a simple service running on the same host but a different port.
The service is a simple Express app.
server.js:
const express = require('express')
const app = express()
const port = 3010
const response = {
message: "hello"
}
app.get('/api/all', (_req, res) => {
res.send(JSON.stringify(response))
})
app.listen(port, () => {
console.log(`listening on port ${port}`)
})
Since the service is running on port 3010, which is different from the Astro site, I configure a server proxy at the Vite level.
astro.config.mjs:
import { defineConfig } from 'astro/config';
import react from '#astrojs/react';
export default defineConfig({
integrations: [react()],
vite: {
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
},
server: {
proxy: {
'/api/all': 'http://localhost:3010'
}
}
},
});
Here is where I am trying to invoke the service.
index.astro:
---
const response = await fetch('/api/all');
const data = await response.json();
console.log(data);
---
When I run yarn dev I get this console output:
Response {
size: 0,
[Symbol(Body internals)]: {
body: Readable {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
stream: Readable {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
_read: [Function (anonymous)],
[Symbol(kCapture)]: false
},
boundary: null,
disturbed: false,
error: null
},
[Symbol(Response internals)]: {
type: 'default',
url: undefined,
status: 404,
statusText: '',
headers: { date: 'Tue, 02 Aug 2022 19:41:02 GMT' },
counter: undefined,
highWaterMark: undefined
}
}
It looks like the network request is returning a 404.
I'm not seeing in the doc much more about server configuration.
Am I going about this the right way?
I have this working correctly with a vanilla Vite app and the same config/setup.
How can I proxy local service calls for an Astro application?
Short Answer
You cannot proxy service calls with Astro but also you don't have to
For direct resolution answer see section functional test without proxy
Details
Astro does not forward the server.proxy config to Vite (unless you patch your own version of Astro), the Astro Vite server config can be seen empty
proxy: {
// add proxies here
},
reference https://github.com/withastro/astro/blob/8c100a6fe6cc652c3799d1622e12c2c969f30510/packages/astro/src/core/create-vite.ts#L125
there is a merge of Astro server with Astro vite.server config but it does not take the proxy param. This is not obvious to get from the code, see tests later.
let result = commonConfig;
result = vite.mergeConfig(result, settings.config.vite || {});
result = vite.mergeConfig(result, commandConfig);
reference https://github.com/withastro/astro/blob/8c100a6fe6cc652c3799d1622e12c2c969f30510/packages/astro/src/core/create-vite.ts#L167
Tests
Config tests
I tried all possible combinations of how to input config to Astro and in each location a different port number to show which one takes an override
a vite.config.js file on root with
export default {
server: {
port:6000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
in two locations in the root file astro.config.mjs
server
vite.server
export default defineConfig({
server:{
port: 3000,
proxy: {
'/api': 'http://localhost:4000'
}
},
integrations: [int_test()],
vite: {
optimizeDeps: {
esbuildOptions: {
define: {
global: 'globalThis'
}
}
},
server: {
port:5000,
proxy: {
'/api': 'http://localhost:4000'
}
}
}
});
in an Astro integration
Astro has a so called integration that helps update the config (sort of Astro plugins) the integration helps identify what was finally kept in the config and also gives a last chance to update the config
integration-test.js
async function config_setup({ updateConfig, config, addPageExtension, command }) {
green_log(`astro:config:setup> running (${command})`)
updateConfig({
server:{proxy : {'/api': 'http://localhost:4000'}},
vite:{server:{proxy : {'/api': 'http://localhost:4000'}}}
})
console.log(config.server)
console.log(config.vite)
green_log(`astro:config:setup> end`)
}
this is the output log
astro:config:setup> running (dev)
{ host: false, port: 3000, streaming: true }
{
optimizeDeps: { esbuildOptions: { define: [Object] } },
server: { port: 5000, proxy: { '/api': 'http://localhost:4000' } }
}
astro:config:setup> end
the proxy parameter is removed from astro server config, the vite config is visible but has no effect as it is overridden, and not forwarded to Vite
test results
dev server runs on port 3000 which is from Astro config server all other configs overridden
the fetch api fails with the error
error Failed to parse URL from /api
File:
D:\dev\astro\astro-examples\24_api-proxy\D:\dev\astro\astro-examples\24_api-proxy\src\pages\index.astro:15:20
Stacktrace:
TypeError: Failed to parse URL from /api
at Object.fetch (node:internal/deps/undici/undici:11118:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
functional test without proxy
Given that Astro front matter runs on the server side, in SSG mode during build and in SSR mode on page load on the server then the server sends the result html, Astro has access to all host ports and can directly use the service port like this
const response = await fetch('http://localhost:4000/api');
const data = await response.json();
console.log(data);
The code above runs as expected without errors
Reference Example
All tests and files mentioned above are available on the reference example github repo : https://github.com/MicroWebStacks/astro-examples/tree/main/24_api-proxy
You can add your own proxy middleware with the astro:server:setup hook.
For example use http-proxy-middleware in the server setup hook.
// plugins/proxy-middleware.mjs
import { createProxyMiddleware } from "http-proxy-middleware"
export default (context, options) => {
const apiProxy = createProxyMiddleware(context, options)
return {
name: 'proxy',
hooks: {
'astro:server:setup': ({ server }) => {
server.middlewares.use(apiProxy)
}
}
}
}
Usage:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import proxyMiddleware from './plugins/proxy-middleware.mjs';
// https://astro.build/config
export default defineConfig({
integrations: [
proxyMiddleware("/api/all", {
target: "http://localhost:3010",
changeOrigin: true,
}),
],
});

Upload image with twitter lite

I'm using twitter-lite library and I want to twit an image with text.
I'm able to twit only text, but My goal is twit with text and image.
At the moment I'm trying to use a static image like:
const fs = require("fs")
const imageData = fs.readFileSync("./public/images/watermark_update_idea.png")
const mediaUploadResponse = await client.post('statuses/upload', {
media:imageData,
}).catch(err => {
console.error(err);
});
But I get that error:
{
_headers: Headers {
[Symbol(map)]: [Object: null prototype] {
'cache-control': [Array],
connection: [Array],
'content-encoding': [Array],
'content-length': [Array],
'content-type': [Array],
date: [Array],
server: [Array],
'set-cookie': [Array],
'strict-transport-security': [Array],
'x-connection-hash': [Array],
'x-response-time': [Array],
'x-tsa-request-body-time': [Array]
}
},
errors: [ { message: 'Sorry, that page does not exist', code: 34 } ]
}
My main goal is to twit with buffer, But I get the same error.
I tried to replicate this test but without lucky.
Potentially two issues here:
Use different subdomains for uploading an image and for posting a tweet:
const Twitter = require("twitter-lite");
const client = Twitter({/* auth data */});
const upload = Twitter({/* auth data */, subdomain: "upload"});
You have to convert your image content to a base64 string:
const imageData = fs.readFileSync("./public/images/watermark_update_idea.png");
const { media_id_string } = await upload.post('media/upload', {
media: imageData.toString("base64")
});

read ECONNRESET error occurs in nextjs geinitialProps when sending get request to api

This error occured in next.js app when i send a get request using axios in getInitialPros of _app.js file.
if (typeof window === "undefined") {
// user = await checkAuth(ctx);
// const token = ctx.req.headers.cookie;
console.log("TOKEN", ctx.req.headers);
if (ctx.req && ctx.req.headers.cookie) {
try {
res = await axiosClient("get", { cookie: ctx.req.headers.cookie }).get(
"/auth/currentuser"
);
user = res.data;
console.log("USER IN SERVER SIDE", user);
ctx.store.dispatch(setAuthenticatedUser(res.data));
} catch (err) {
console.log("ERROR in APP", err);
// console.log("USER FOUND IN APP.JS", res.data);
ctx.store.dispatch(removeAuthenticatedUser());
}
}
} else {
try {
res = await axiosClient("get").get("/auth/currentuser");
user = res.data;
// await checkAuth(ctx);
// await checkAuth(ctx,)
console.log("IN CLIENT", res.data);
} catch (err) {}
}
this error occurred when the page is refreshed but it only occurs on server side, not in client side.
ERROR in APP Error: read ECONNRESET
at TLSWrap.onStreamRead (internal/stream_base_commons.js:205:27) {
errno: 'ECONNRESET',
code: 'ECONNRESET',
syscall: 'read',
config: {
url: '/auth/currentuser',
method: 'get',
headers: {
Accept: 'application/json, text/plain, */*',
cookie: 'token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1ZjNhYTJlMmQxN2YxMzAxYTA0NGUxYTIiLCJpYXQiOjE1OTgyODUyMDMsImV4cCI6MTU5ODI4ODgwM30.qtaW-D9P6tJHzL1uHZs3wlzF39UPVkPTLEieuqaVEJY',
'User-Agent': 'axios/0.19.2'
},
baseURL: 'https://tatkaladda.com/api/',
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
adapter: [Function: httpAdapter],
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined
},
this error only occurred in production app not in development mode.
Node.js is not aware of any baseURL. Your browser is. So on the server side you have to provide the full path, on the client side when using relative links they're relative to the base url, eg https://example.com
By: Tim Neutkens - Co-author of Next.js and MDX
One of the suggested workaround is to use full path in the getInitialProps when using axios. In your case change this:
res = await axiosClient("get").get("/auth/currentuser");
to
res = await axiosClient("get").get("http://localhost:3000/auth/currentuser");
//or use external domian if you not on localhost i.e. https:api.example.com/auth/currentuser
If still this does not work, use axios API and set full path on baseUrl as follow:
// axios call
await axios({
method: 'GET',
url: 'http://abc.herokuapp.com/api/' //or baseURL: 'http://abc.herokuapp.com/api/'
)}
Have time! kindly read this, it might help: https://github.com/vercel/next.js/issues/5009
UPDATE
Also you can try to construct your baseURL from getInitialProps context
async getInitialProps({ req }) {
const protocol = req.headers['x-forwarded-proto'] || 'http'
const baseUrl = req ? `${protocol}://${req.headers.host}` : ''
const res = await fetch(baseUrl + '/api/recent/1')
...
}

Node JS REST call Error: self signed certificate in certificate chain

I am trying to call secure REST service of other application. Also providing client certificate, but getting error as below:
request function
Response: IncomingMessage {
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
.........
authorized: false,
**authorizationError: 'SELF_SIGNED_CERT_IN_CHAIN',**
encrypted: true,
..........
I tried to test it with rest client using postman and getting the response.
But not working through above Node JS program/ code.
So as per my understanding this is happening due to SSL-intercepting proxy; npm detects this and complains.
I have implemented the Rest client in the Node JS application with POST method to consume the REST service is as below.
var https = require('https');
var fs = require('fs');'
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var options = {
host: '<HOSTNAME>',
port: '<PORT>',
path: '<PATH>',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': '',
},
ca: [ fs.readFileSync('<.jks file/ certificate name>') ],
checkServerIdentity: function (host, cert) {
},
rejectUnauthorized:false,
body: ''
};
var req = https.request(options, function(res) {
console.log('request function')
console.log("Response: ", res);
res.on('data', function(d) {
'' +
'?' });
});
req.end();
req.on('error', function(e) {
console.error(e);
});
I tried other solutions like
"npm config set strict-ssl false" command but could not work.
I also tried with
rejectUnauthorized:false and process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
as you can see in my code but nothing is working so far.
Few required entries in .npmrc file are as below:
registry=https://registry.npmjs.org/
strict-ssl=false
cafile= <certificate path>
I tried all the possibilities but getting error as mentioned, please suggest if I missed anything or need to update anything. Thank you.
One solution I found is I can ignore the host name check to consume the Rest service using Node JS, but not able to find how can we ignore host name with above Node JS code.

Accessing Loopback config data from middleware

Say we are in Loopback middleware, such as
app.use(function(req,res,next){
// I am not sure what key to use here in the getter...
const config = app.get('?');
});
I want to access the overall config that Loopback is using.
I tried:
const config = app.get('config');
const config = app.get('env');
const config = app.get('datasources');
nothing gives me quite what I want.
Interestingly, this gives me:
console.log(app.get('restApiRoot'));
=> '/api'
so that's a clue to what's going on, but I want to get the parent object(s) for the above data.
how can we access the configuration that Loopback has loaded. The configuration of course varies by environment variables etc.
I want to log what datasources.x.js file was loaded and what config.x.js file was loaded, and any other server configuration info I can capture.
Having a lot of trouble figuring out how to do this.
This seems to be the same question I have:
https://github.com/strongloop/loopback/issues/1526
but they point me to the void that is Google Groups and I searched through there and couldn't find what the answer to this question.
This behavior is actually inherited from Express.
The entire config is stored in the app.settings object, with app.get(key) and app.set(key,value) just acting as getter/setter.
Doing console.log(app.settings); (in server/server.js for instance) it on a fresh loopback install returns the following:
{ 'x-powered-by': true,
etag: 'weak',
'etag fn': [Function: wetag],
env: 'development',
'query parser': 'extended',
'query parser fn': [Function: parseExtendedQueryString],
'subdomain offset': 2,
'trust proxy': false,
'trust proxy fn': [Function: trustNone],
view: [Function: View],
views: 'C:\\Users\\*******\\Documents\\GitHub\\lbtest\\views',
'jsonp callback name': 'callback',
host: '0.0.0.0',
port: 3000,
restApiRoot: '/api',
remoting:
{ context: { enableHttpContext: false },
rest: { normalizeHttpPath: false, xml: false },
json: { strict: false, limit: '100kb' },
urlencoded: { extended: true, limit: '100kb' },
cors: false,
errorHandler: { disableStackTrace: false } },
legacyExplorer: false,
'loopback-component-explorer': { mountPath: '/explorer' },
url: 'http://localhost:3000/' }

Resources