How to access form body in oak/deno - deno

I'm using oak/deno. I have a form that is submitted from an ejs file served up. How do I access the form body? When I log it to the console, it prints: {type: "form", value: URLSearchParamsImpl {} }
The post handler is shown below:
router.post("/add", async (ctx: RouterContext) => {
const body = (await ctx.request.body())
console.log(body)
ctx.response.redirect("/");
});

If you're sending x-www-form-urlencoded just use URLSearchParams instance available in body.value.
body.value.get('yourFieldName')
If body.type === "form-data" you can use .value.read() and you'll get the multipart/form-data fields
router.post("/add", async (ctx: RouterContext) => {
const body = await ctx.request.body({ type: 'form-data '});
const formData = await body.value.read();
console.log(formData.fields);
ctx.response.redirect("/");
});

something like this returns the values
it looked like body.value is accessed through .get(<key>) or can be iterated with .entries() or Object.fromEntries()
async register(context: RouterContext) {
const body = context.request.body({ type: 'form' })
const value = await body.value
console.log(value.get('email'))
for (const [key, val] of value.entries()) {
console.log(key, val)
}
const args = Object.fromEntries(value)
console.log(args)
context.response.body = 'test'
}

Related

Telegram bot won't respond even doe webhook is set

The code for the bot is currently hosted on a Cloudflare worker, and there are no errors being reported from that end. Additionally, upon investigation of the Botfather side, everything seems to be functioning normally as well. However, despite attempting various solutions such as changing bots, tokens, and chat groups, the issue remains.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const { pathname } = new URL(request.url)
if (pathname === '/') {
return new Response('Hello! This is a Telegram bot. Send me a message.')
}
const { text } = await request.json()
if (text.startsWith('/start')) {
return new Response('Welcome to the bot! Use the /help command to see available options.')
} else if (text.startsWith('/help')) {
return new Response('Available commands:\n/scrape - scrapes videos from the specified website and sends them to the Telegram chat.\n/<example> - scrapes posts from the specified website and sends them to the Telegram chat.')
} else if (text.startsWith('/scrape')) {
const videoUrl = await scrapeVideoUrl('<example url>')
const message = `Here's the latest video: ${videoUrl}`
await sendMessageToChatId(message)
return new Response('OK')
} else if (text.startsWith('/<example>')) {
const post = await scrapePost('<example url>')
const message = `Here's the latest post: ${post.title}\n${post.url}`
await sendMessageToChatId(message)
return new Response('OK')
} else {
return new Response('Invalid command. Use the /help command to see available options.')
}
}
async function scrapeVideoUrl(url) {
const response = await fetch(url)
const html = await response.text()
const cheerio = require('cheerio')
const $ = cheerio.load(html)
const videoUrl = $('div.media > a').attr('href')
return videoUrl
}
async function scrapePost(url) {
const response = await fetch(url)
const html = await response.text()
const cheerio = require('cheerio')
const $ = cheerio.load(html)
const post = {
title: $('div.content h1').text(),
url: url
}
return post
}
async function sendMessageToChatId(message) {
const telegramApiUrl = "https://api.telegram.org/bot<token>/sendMessage";
const chatId = "<id>";
const response = await fetch(telegramApiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
chat_id: chatId,
text: message
})
})
}
There is zero response on the telegram side, even doe on web side everything seems to be fine.

Can i create a Nextjs dynamic route [id]-[first_name]-[last_name]?

I am using nextjs to build a directory. I effectively want to click on 'more info' and an info page to load under the URL of /info/[id]-[first_name]-[last_name].
I am pulling data from an api by id, which will then get the first_name and last_name data.
I have a file inside an info folder named [id]-[first_name]-[last_name] :
export default function Info({ info }) {
return (
<div>
<h1>First Name</h1>
<p> Last Name </p>
</div>
);
}
export const getStaticPaths = async () => {
const res = await fetch('http://xxx:1337/api/info');
const data = await res.json();
// map data to an array of path objects with params (id)
const paths = [data].map(info => {
return {
params: [{
id: `${info.id}-`,
first_name: `${info.first_name}-`,
last_name: `${info.last_name}`
}]
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch('http://xxxx:1337/api/info/' + id);
const data = await res.json();
return {
props: { info: data }
}
With this I just get the error:
Error: A required parameter (id]-[first_name]-[last_name) was not provided as a string in getStaticPaths for /info/[id]-[first_name]-[last_name]
I guess that error is pretty self-explanatory, but I am blocked at this point. I have seen that i may be able to use a slug, but that means re-working a lot of the api.
Any direction with this is apprecated. Thanks!
in this way you can catch all attributes /info/[id]/[first_name]/[last_name]
by making the file /info/[...slug]
export const getStaticProps = async ({ query }) => {
const [id ,firstname ,lastname] = query.slug }
or keep it /info/[slug] and get it as string after that you can split it

Post request to normal route, not using API middleware

Below my pages directory i have a few routes (for example "/product/details").
I'm using getServerSideProps() to have the page render server side.
How can i send a POST request containing data in the body to this page directly?
The idea would be that i can do something like this:
export async function getServerSideProps(postData) {
return {
props: {postData.body},
}
}
I've tried console logging "postData". I can see that the post request headers are being sent, but the request body is missing.
Thanks
Edit:
I'm doing the posting using Postman, and i'm sending a raw body of type JSON containing a single key:value. But as i said, the page doesn't seem to receive the posted data.
Here is a code snippet for how i'm sending a post request to a route using puppeteer:
const page = await puppeteerConnection.newPage();
await page.setRequestInterception(true);
await page.once('request', (request) => {
let data = {
'method': 'POST',
'postData': JSON.stringify(jsonData),
'headers': {
...request.headers(),
'Content-Type': 'application/json'
},
};
request.continue(data);
page.setRequestInterception(false);
});
await page.goto('pathToNextJSRoute');
getServerSideProps() accepts a context parameter (which you've named postData in your example) and one of the keys in that object (req) contains the request body you're looking for. It arrives as a readable stream of byte data, though, so you'll need to convert it first:
const streamToString = async (stream) => {
if (stream) {
const chunks = [];
for await (const chunk of stream) {
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString("utf-8");
}
return null;
};
export async function getServerSideProps(context) {
let data = null;
if (context.req.method === "POST") {
const body = await streamToString(context.req);
data = JSON.parse(body);
}
console.log(data);
return {
props: { data },
};
}

Next JS API calls with auth0

I have a question about auth0 and next js.
For example, I have the next code (this code works)
//initialprops enables server-side rendering in a page and allows you to do initial data population
ModelsList.getInitialProps = async (ctx) => {
//this is static token to test from auth0.com
const accessToken = 'eyJhbG.....'
//fetching data
const res = await fetch('http://localhost:7071/api/bo/getModels', {
headers: {
Authorization: `Bearer ${accessToken}`
}
})
const json = await res.json()
return { data: json }
}
As you can see, I have accessToken variable as a text. It's a problem for me
How can make accessToken dynamic?
Thanks a lot!
P.S please, dont reference to auth0 documentation, I have tried a lot. Provide, please, a real solution/example.
Ok, so this is what worked for me.
Let's say you've got api.example.com/resources. This where data actually is. You will need to proxy via next's api.
Inside your jsx component, you fetch next's api.
// components/Dashboard.jsx
const API_URL = "api/resources";
async function fetcher(url: any) {
const res = await fetch(url);
const json = await res.json();
return json;
}
function Dashboard() {
const { data, error } = useSWR(API_URL, fetcher);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return <div>show your resources here</div>;
}
and now inside the next's api file you can fetch the actual endpoint you need.
// api/resources.js
import {
getAccessToken,
getSession,
withApiAuthRequired,
} from "#auth0/nextjs-auth0";
export default withApiAuthRequired(async function healthcheck(req, res) {
const session = await getSession(req, res);
const token = session?.idToken;
const response = await fetch("https://api.example.com/resources", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
res.status(200).json(data);
});
if you get errors, check the jwts you're getting. Audience or scope mismatch errors are usually the main culprits.

Handling errors with redux-toolkit

The information about the error in my case sits deeply in the response, and I'm trying to move my project to redux-toolkit. This is how it used to be:
catch(e) {
let warning
switch (e.response.data.error.message) {
...
}
}
The problem is that redux-toolkit doesn't put that data in the rejected action creator and I have no access to the error message, it puts his message instead of the initial one:
While the original response looks like this:
So how can I retrieve that data?
Per the docs, RTK's createAsyncThunk has default handling for errors - it dispatches a serialized version of the Error instance as action.error.
If you need to customize what goes into the rejected action, it's up to you to catch the initial error yourself, and use rejectWithValue() to decide what goes into the action:
const updateUser = createAsyncThunk(
'users/update',
async (userData, { rejectWithValue }) => {
const { id, ...fields } = userData
try {
const response = await userAPI.updateById(id, fields)
return response.data.user
} catch (err) {
if (!err.response) {
throw err
}
return rejectWithValue(err.response.data)
}
}
)
We use thunkAPI, the second argument in the payloadCreator; containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options: For our example async(obj, {dispatch, getState, rejectWithValue, fulfillWithValue}) is our payloadCreator with the required arguments;
This is an example using fetch api
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
export const getExampleThunk = createAsyncThunk(
'auth/getExampleThunk',
async(obj, {dispatch, getState, rejectWithValue, fulfillWithValue}) => {
try{
const response = await fetch('https://reqrefs.in/api/users/yu');
if (!response.ok) {
return rejectWithValue(response.status)
}
const data = await response.json();
return fulfillWithValue(data)
}catch(error){
throw rejectWithValue(error.message)
}
}
)
Simple example in slice:
const exampleSlice = createSlice({
name: 'example',
initialState: {
httpErr: false,
},
reducers: {
//set your reducers
},
extraReducers: {
[getExampleThunk.pending]: (state, action) => {
//some action here
},
[getExampleThunk.fulfilled]: (state, action) => {
state.httpErr = action.payload;
},
[getExampleThunk.rejected]: (state, action) => {
state.httpErr = action.payload;
}
}
})
Handling Error
Take note:
rejectWithValue - utility (additional option from thunkAPI) that you can return/throw in your action creator to return a rejected response with a defined payload and meta. It will pass whatever value you give it and return it in the payload of the rejected action.
For those that use apisauce (wrapper that uses axios with standardized errors + request/response transforms)
Since apisauce always resolves Promises, you can check !response.ok and handle it with rejectWithValue. (Notice the ! since we want to check if the request is not ok)
export const login = createAsyncThunk(
"auth/login",
async (credentials, { rejectWithValue }) => {
const response = await authAPI.signin(credentials);
if (!response.ok) {
return rejectWithValue(response.data.message);
}
return response.data;
}
);

Resources