yahooFinance module with sapper - javascript

I am making a project where I want to use darta from yahooFinance.
I have found this project https://www.npmjs.com/package/yahoo-finance.
I have also used the basic sapper template.
Basically what I am trying is to retrive data from YF and show them on the FE.
I gave this piece of code:
<script>
import yahooFinance from 'yahoo-finance';
let response;
async function searchStock (){
yahooFinance.historical({
symbol: 'AAPL',
from: '2020-01-01',
to: '2020-12-31',
}, function (err, quotes) {
console.log(quotes)
});
}
</script>
But everytime I try to compile I get:
Unexpected token (Note that you need #rollup/plugin-json to import JSON files)
1: {
2: "version": "2020d",
^
3: "zones": [
4: "Africa/Abidjan|LMT GMT|g.8 0|01|-2ldXH.Q|48e5",
So I gave tried to import it thus way var yahooFinance = require('yahoo-finance');
But then I get Uncaught (in promise) ReferenceError: require is not defined in to the console.

You won't be able to use the yahoo-finance package on the front end, since it uses Node APIs. Since you're using Sapper, you can use the package in a server route and fetch it from the client.
Create the file yahoo.json.js and place it in src/routes. Then copy + paste the following into it. This will call the historical method from yahoo-finance and return the result as JSON.
import yahooFinance from 'yahoo-finance';
export async function get(req, res, next) {
const response = await new Promise((resolve, reject) => {
yahooFinance.historical({
symbol: 'AAPL',
from: '2020-01-01',
to: '2020-12-31',
}, function (err, quotes) {
if (err) reject(err);
resolve(quotes);
});
})
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(response));
}
You can then call this server route from within a Svelte component. This uses the Sapper preload method to fetch the data before the page renders.
<script context="module">
export async function preload() {
const res = await this.fetch('/yahoo.json');
const data = await res.json();
return {data};
}
</script>
<script>
export let data;
</script>
{JSON.stringify(data)}
You will likely want to enhance the server route to add request parameters and better error handling, but this shows you how to get it working.

Related

Uncaught (in promise) TypeError: (intermediate value).findOne is not a function

When trying to call getStocks from a Vue component I get the error stated above.
smileCalc:
import User from "../models/user.js";
let userID = "62e6d96a51186be0ad2864f9";
let userStocks;
async function getUserStocks() {
await User.findOne({ _id: userID }, (err, user) => {
if (user != null || user != undefined) {
userStocks = user.stocks;
}
}).clone();
};
export async function getStocks() {
await getUserStocks();
return userStocks;
}
Vue Component:
<script>
import { getStocks } from "../../../backend/scripts/smileCalc.js";
export default {
methods: {
getStocks,
},
};
</script>
<template>
<h1>User Stocks: {{ getStocks() }}</h1>
</template>
I know that the Schema is defined, exported, and imported correctly because I do not get the error when I execute the script and it works normally. The solution that I have found for TypeErrors similar to mine is to add a semicolon to the end of the function, but that did not fix my issue. I also tried using findById rather than findOne and got the same error.
Update: The issue I was having was trying to access my mongoose schema on the client side which was hosted on port 3000 while my backend was being hosted on 4000. I needed to implement an HTTP request to interact between the two.

Next js API + MongoDB error 431 on dynamic request

I'm getting a 431 (headers fields too large) on some API calls within a fullstack Next JS project. This only occurs on a dynamic API route (/author/get/[slug]), same result with both frontend and Postman. The server is running on local, and other endpoints works fine with exactly the same fetching logic.
The request is not even treated by Next API, no log will appear anywhere.
The database used is mongoDB. The API is pure simple JS.
The objective is to get a single author (will evolve in getStaticProps)
The API call looks like this (no headers whatsoever):
try {
const res = await fetch(`http://localhost:3000/api/author/get/${slug}`, { method: "GET" })
console.log(res)
} catch (error) { console.log(error) }
And the endpoint:
// author/get/[slug].js
import {getClient} from "../../../../src/config/mongodb-config";
export default async function handler(req, res) {
const { query } = req
const { slug } = query
if(req.method !== 'GET') {
return
}
const clientPromise = await getClient()
const author = clientPromise.db("database").collection("authors").findOne({ 'slug': slug })
res.status(200).json(author)
await clientPromise.close()
}
Tried without success:
To remove a nesting level (making the path /author/[slug])

NextJS - TypeError: res.unstable_revalidate is not a function

I try to use a brand new feature released in NextJS v.12.1 https://deepinder.me/nextjs-on-demand-isr. The API itself works fine. I can reach it. But in exchange it returns 500 error that says res.unstable_revalidate is not a function. It does not work either over dev (next server && next dev) run or production one (next build && next start).
This is the api endpoint:
// ./api/misc/revalidate
const revalidateCache = async (_, res) => {
console.log(res.unstable_revalidate, 'TEST REVALIDATE'); // res.unstable_revalidate is undefined here :(
try {
await res.unstable_revalidate('/');
return res.json({ revalidated: true });
} catch (err) {
return res.status(500).send(`Error revalidating: ${err}`);
}
};
export default revalidateCache;
This is the invoke:
// ./apps/client/services/server
const getRevalidate = async () => {
await fetch('/api/misc/revalidate');
};
export default getRevalidate;
View layer that I call from:
// .src/pages/index.js
// ...some code here
const HomePage = ({ page, legacy }) => {
const handleClick = () => {
getRevalidate();
};
return (
<div className={styles.homeRoot}>
<button onClick={handleClick}>REVALIDATE</button>
</div>
);
};
UPD:
I use express to handle API abstration.
import express from 'express';
import revalidateCacheApi from './api/misc/revalidate';
export default app => {
// ...some code here
app.use('/api/misc/revalidate', revalidateCacheApi);
};
NVM. It was an issue with my local server. I use advanced set up with two independent instances (:3000 and :4000) spinning in the memory.
The way I designed API above suppose to call it over :4000 server. Which is in fact Express server (obviously does not has NextJS internal API to purge the cache).
So I moved the call to the pages/api/revalidate and up to :3000 server.
Works fine:
// ./src/pages/server/revalidate.js
const getRevalidate = async () => {
await fetch('/api/revalidate');
};
export default getRevalidate;

nextjs route middleware for authentication

I'm trying to figure out an appropriate way of doing authentication, which I know is a touchy subject on the GitHub issue page.
My authentication is simple. I store a JWT token in the session. I send it to a different server for approval. If I get back true, we keep going, if I get back false, it clears the session and puts sends them to the main page.
In my server.js file I have the following (note- I am using the example from nextjs learn and just adding isAuthenticated):
function isAuthenticated(req, res, next) {
//checks go here
//if (req.user.authenticated)
// return next();
// IF A USER ISN'T LOGGED IN, THEN REDIRECT THEM SOMEWHERE
res.redirect('/');
}
server.get('/p/:id', isAuthenticated, (req, res) => {
const actualPage = '/post'
const queryParams = { id: req.params.id }
app.render(req, res, actualPage, queryParams)
})
This works as designed. If I refresh the page /p/123, it will redirect to the /. However, if I go there via a next/link href, it doesn't. Which I believe is because it's not using express at this point but next's custom routing.
Is there a way I can bake in a check for every single next/link that doesn't go through express so that I can make sure the user is logged in?
Tim from the next chat helped me solve this. Solution can be found here but I will quote him so you all can see:
You can do the check in _app.js getInitialProps and redirect like this
Example of how to use it
_app.js documentation
I've also created an example skeleton template you can take a look at.
--
EDIT July 2021 - WARNING: This is an outdated solution and has not been confirmed to work with the latest versions of next.js. Use skeleton template at your own risk.
Edit: Updated answer for Next 12.2+
Note: The below contents is copied from the official blog post since SO generally discourages links that can become stale/dead over time
https://nextjs.org/blog/next-12-2#middleware-stable
Middleware is now stable in 12.2 and has an improved API based on feedback from users.
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
// If the incoming request has the "beta" cookie
// then we'll rewrite the request to /beta
export function middleware(req: NextRequest) {
const isInBeta = JSON.parse(req.cookies.get('beta') || 'false');
req.nextUrl.pathname = isInBeta ? '/beta' : '/';
return NextResponse.rewrite(req.nextUrl);
}
// Supports both a single value or an array of matches
export const config = {
matcher: '/',
};
Migration guide
https://nextjs.org/docs/messages/middleware-upgrade-guide
Breaking changes
No Nested Middleware
No Response Body
Cookies API Revamped
New User-Agent Helper
No More Page Match Data
Executing Middleware on Internal Next.js Requests
How to upgrade
You should declare one single Middleware file in your application, which should be located next to the pages directory and named without an _ prefix. Your Middleware file can still have either a .ts or .js extension.
Middleware will be invoked for every route in the app, and a custom matcher can be used to define matching filters. The following is an example for a Middleware that triggers for /about/* and /dashboard/:path*, the custom matcher is defined in an exported config object:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
// Supports both a single string value or an array of matchers
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
Edit: Outdated answer for next > 12 and < 12.2
With the release of Next.js 12, there's now beta support for middleware using Vercel Edge Functions.
https://nextjs.org/blog/next-12#introducing-middleware
Middleware uses a strict runtime that supports standard Web APIs like fetch. > This works out of the box using next start, as well as on Edge platforms like Vercel, which use Edge Functions.
To use Middleware in Next.js, you can create a file pages/_middleware.js. In this example, we use the standard Web API Response (MDN):
// pages/_middleware.js
export function middleware(req, ev) {
return new Response('Hello, world!')
}
JWT Authentication example
https://github.com/vercel/examples/tree/main/edge-functions/jwt-authentication
in next.config.js:
const withTM = require('#vercel/edge-functions-ui/transpile')()
module.exports = withTM()
in pages/_middleware.js:
import { NextRequest, NextResponse } from 'next/server'
import { setUserCookie } from '#lib/auth'
export function middleware(req: NextRequest) {
// Add the user token to the response
return setUserCookie(req, NextResponse.next())
}
in pages/api/_middleware.js:
import type { NextRequest } from 'next/server'
import { nanoid } from 'nanoid'
import { verifyAuth } from '#lib/auth'
import { jsonResponse } from '#lib/utils'
export async function middleware(req: NextRequest) {
const url = req.nextUrl
if (url.searchParams.has('edge')) {
const resOrPayload = await verifyAuth(req)
return resOrPayload instanceof Response
? resOrPayload
: jsonResponse(200, { nanoid: nanoid(), jwtID: resOrPayload.jti })
}
}
in pages/api/index.js:
import type { NextApiRequest, NextApiResponse } from 'next'
import { verify, JwtPayload } from 'jsonwebtoken'
import { nanoid } from 'nanoid'
import { USER_TOKEN, JWT_SECRET_KEY } from '#lib/constants'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({
error: { message: 'Method not allowed' },
})
}
try {
const token = req.cookies[USER_TOKEN]
const payload = verify(token, JWT_SECRET_KEY) as JwtPayload
res.status(200).json({ nanoid: nanoid(), jwtID: payload.jti })
} catch (err) {
res.status(401).json({ error: { message: 'Your token has expired.' } })
}
}
There is no middleware for no API routes in NextJS, but there are HOCs, which you can use to connect to db - select the user, etc:
https://hoangvvo.com/blog/nextjs-middleware

Getting 204 using koa2 and koa-router for REST api - response body not being passed

I'm coming from Express and trying to learn Koa2 for a new project that I'm working on, but I'm struggling getting the most basic Get operation working for my app.
On the server side I have a route setup that is hitting an authorization server (Etrade), which returns an HTML link that the user will need to use to authorize the app.
I can use Postman to hit the route and see that I get the link back from Etrade through my console.log() call, but it is not coming back to Postman in the response body.
When I wired it up to the client app, I get a response status code of 204, which means my response body is empty if I'm understanding this correctly.
I need to figure out how to get the response body passed along as well as improve my understanding of Koa2.
I've currently setup my server.js as follows:
import Koa from 'koa';
import convert from 'koa-convert';
import proxy from 'koa-proxy';
import logger from 'koa-logger';
import body from 'koa-better-body';
import api from '../config/router/router';
import historyApiFallback from 'koa-connect-history-api-fallback';
import config from '../config/base.config';
const port = config.server_port;
const host = config.server_host;
const app = new Koa();
app.use(logger());
app.use(body());
app.use(api.routes());
app.use(api.allowedMethods());
// enable koa-proxyy if it has been enabled in the config
if ( config.proxy && config.proxy.enabled ) {
app.use(convert(proxy(config.proxy.options)));
}
app.use(convert(historyApiFallback({
verbose : false
})));
server.listen(port);
console.log(`Server is now running at http://${host}:${port}.`);
My router.js is setup as follows:
import Router from 'koa-router';
import etradeVerification from '../../server/api/etrade/verification';
const api = new Router({
prefix: '/api'
});
etradeVerification(api);
export default api;
Finally the logic for the route, minus the key and secret stuff:
import Etrade from 'node-etrade-api';
const myKey = '';
const mySecret = '';
const configuration = {
useSandbox : true,
key : myKey,
secret : mySecret
};
const et = new Etrade(configuration);
export default function( router ) {
router.get('/etrade', getEtradeUrl);
}
async function getEtradeUrl( ctx, next ) {
// Isn't this how I send the response back to the client?
// This isn't coming through as a response body when using Postman or the client app
ctx.body = await et.getRequestToken(receiveVerificationUrl, failedToGetUrl);
}
function receiveVerificationUrl( url ) {
console.log(url); // This works and displays the response from etrade
return url
}
function failedToGetUrl( error ) {
console.log('Error encountered while attempting to retrieve a request token: ', error);
}
Thanks for your help and guidance!
ctx.body = await et.getRequestToken(receiveVerificationUrl,
failedToGetUrl);
This call to et.getRequestToken does not return anything. When await fires, it'll just get undefined. Normally I'd suggest using es6-promisify but it's also not a standard Node interface either (one callback, with an err and value arguments (Very disappointing!))
Perhaps create a function like the following to Promisify the function:
function getRequestToken(et){
return new Promise(function(resolve, reject){
et.getRequestToken(resolve, reject)
})
}
Then you can ctx.body = await getRequestToken(et).

Categories