Now, i'm in http://localhost:3000/, but on prod i will be in a different url, for example http://example.com/, how can i get full browser url in getServerSideProps? I have to get http://localhost:3000/ or http://example.com/, if string will not contain port, it will be ok
And in the additional answer you use in this my case :
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const { req } = context;
let url = req.headers.referer;
let arr = url.split('/');
url = `${arr[0]}//${arr[2]}`;
This URL gives you for example http://localhost:3000 enjoy!
'next/router' provide a couple of methods too, which you can use. For example:
import { useRouter } from 'next/router';
const RouterObject = () => {
const { asPath, pathname, req, query, res } = useRouter();
console.log(asPath);
console.log(pathname);
console.log('host: ', req.headers.host);
}
If you have the host you can check if contains localhost. Then you know you are in your local environment.
Related
I am currently working on social media mern stack react app. I am using node js and express as my backend services , also using mongoose to store my data and axios and redux thunk which connect the backend to the front end. Till now I had no issue recieving and sending data to the server. Right now I am trying to create search post get request ,base on a keyword the user entered. The issue with it, that when I am sending the keyword to the server instead of recieving the string it gets undefined value, like redux thunk not sending anything. I will be very thankful if someone could help me with that. I am watching the code over and over again and can't find out the reason for that.
My post controller class(I copied only the relevant function):
import express from "express";
const app = express();
import Post from "../model/PostModel.js";
import ErrorHandlng from "../utilities/ErrorHandling.js";
import bodyParser from "body-parser";
import catchAsync from "../utilities/CatchAsync.js";
import User from "../model/UserModel.js";
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
export const getPosts = catchAsync(async (req, res, next) => {
const data = req.body.keyword;
const page = parseInt(req.query.page || "0");
const PAGE_SIZE = 20;
const query = new RegExp(data, "i");
const total = await Post.countDocuments({});
const posts = await Post.find({ $or: [{ title: query }, { content: query }] })
.limit(PAGE_SIZE)
.skip(PAGE_SIZE * page);
if (!posts) {
return next(new ErrorHandlng("No posts were found", 400));
}
res.status(200).json({
status: "success",
data: {
totalPages: Math.ceil(total / PAGE_SIZE),
posts,
},
});
});
My api class(front end,copied only the calling for that specific get request):
import axios from "axios";
const baseURL = "http://localhost:8000";
axios.defaults.withCredentials = true;
const API = axios.create({
baseURL,
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export const getPostsByKeyword = (keyword, page) =>
API.get(`/post/getPostsByKey?page=${page}`, keyword);
Post slice class:
export const fetchPostsByKeyWord = createAsyncThunk(
"post/getKeyword",
async ({ keyword, page }, { fulfillWithValue, rejectWithValue }) => {
try {
const response = await api.getPostsByKeyword(keyword, page);
if (response.statusCode === "400") {
throw new Error("There are no available posts");
}
const fetchData = await response.data.data.posts;
const totalPages = await response.data.data.totalPages;
return fulfillWithValue({ fetchData, totalPages });
} catch (err) {
console.log(err.response.message);
}
}
);
const initialState = { status: "undefined" };
const PostSlice = createSlice({
name: "post",
initialState,
reducers: {},
extraReducers: {},
});
export const postActions = PostSlice.actions;
export default PostSlice;
Calling the backend:
dispatch(fetchPostsByKeyWord({ keyword, page }))
.unwrap()
.then((originalPromiseResults) => {
console.log("thte " + " " + originalPromiseResults.totalPages);
console.log("The data is" + originalPromiseResults.fetchData);
setTotalPages(originalPromiseResults.totalPages);
})
.catch((err) => {
console.log(err.message);
});
As you can see I have not copied the whole code, I copied only the parts that are relevants for the question.
Browsers cannot currently send GET requests with a request body. XMLHttpRequest (which Axios uses) will ignore it and fetch() will trigger an error.
See also HTTP GET with request body for extra discussion on why trying this might be a bad idea.
You should instead pass everything required in the query string, preferably via the params option so it is correctly encoded...
export const getPostsByKeyword = (keyword, page) =>
API.get("/post/getPostsByKey", { params: { page, keyword } });
and grab the data via req.query server-side.
const { page, keyword } = req.query;
With vanilla JS, you can use URLSearchParams to construct the query string...
const params = new URLSearchParams({ page, keyword });
// XHR
const xhr = new XMLHttpRequest();
xhr.open("GET", `/post/getPostsByKey?${params}`);
// Fetch
fetch(`/post/getPostsByKey?${params}`); // GET is the default method
Your Axios instance creation could also be a lot simpler...
Axios is usually quite good at setting the correct content-type header, you don't have to
Your Express app isn't doing any content-negotiation so you don't need to set the accept header
Unless you're actually using cookies (which it doesn't look like), you don't need credential support
const API = axios.create({ baseURL });
I'm trying to make a GitHub webhook server with Deno, but I cannot find any possible way to do the validation.
This is my current attempt using webhooks-methods.js:
import { Application } from "https://deno.land/x/oak/mod.ts";
import { verify } from "https://cdn.skypack.dev/#octokit/webhooks-methods?dts";
const app = new Application();
app.use(async (ctx, next) => {
try {
await next();
} catch (_err) {
ctx.response.status = 500;
}
});
const secret = "...";
app.use(async (ctx) => {
const signature = ctx.request.headers.get("X-Hub-Signature-256");
if (signature) {
const payload = await ctx.request.body({ type: "text" }).value;
const result = await verify(secret, payload, signature);
console.log(result);
}
ctx.response.status = 200;
});
The verify function is returning false every time.
Your example is very close. The GitHub webhook documentation details the signature header schema. The value is a digest algorithm prefix followed by the signature, in the format of ${ALGO}=${SIGNATURE}:
X-Hub-Signature-256: sha256=d57c68ca6f92289e6987922ff26938930f6e66a2d161ef06abdf1859230aa23c
So, you need to extract the signature from the value (omitting the prefix):
const signatureHeader = request.headers.get("X-Hub-Signature-256");
const signature = signatureHeader.slice("sha256=".length);
Update: Starting in release version 3.0.1 of octokit/webhooks-methods.js, it is no longer necessary to manually extract the signature from the header — that task is handled by the verify function. The code in the answer has been updated to reflect this change.
Here's a complete, working example that you can simply copy + paste into a project or playground on Deno Deploy:
gh-webhook-logger.ts:
import { assert } from "https://deno.land/std#0.177.0/testing/asserts.ts";
import {
Application,
NativeRequest,
Router,
} from "https://deno.land/x/oak#v11.1.0/mod.ts";
import type { ServerRequest } from "https://deno.land/x/oak#v11.1.0/types.d.ts";
import { verify } from "https://esm.sh/#octokit/webhooks-methods#3.0.2?pin=v106";
// In actual usage, use a private secret:
// const SECRET = Deno.env.get("SIGNING_SECRET");
// But for the purposes of this demo, the exposed secret is:
const SECRET = "Let me know if you found this to be helpful!";
type GitHubWebhookVerificationStatus = {
id: string;
verified: boolean;
};
// Because this uses a native Request,
// it can be used in other contexts besides Oak (e.g. `std/http/serve`):
async function verifyGitHubWebhook(
request: Request,
): Promise<GitHubWebhookVerificationStatus> {
const id = request.headers.get("X-GitHub-Delivery");
// This should be more strict in reality
assert(id, "Not a GH webhhok");
const signatureHeader = request.headers.get("X-Hub-Signature-256");
let verified = false;
if (signatureHeader) {
const payload = await request.clone().text();
verified = await verify(SECRET, payload, signatureHeader);
}
return { id, verified };
}
// Type predicate used to access native Request instance
// Ref: https://github.com/oakserver/oak/issues/501#issuecomment-1084046581
function isNativeRequest(r: ServerRequest): r is NativeRequest {
// deno-lint-ignore no-explicit-any
return (r as any).request instanceof Request;
}
const webhookLogger = new Router().post("/webhook", async (ctx) => {
assert(isNativeRequest(ctx.request.originalRequest));
const status = await verifyGitHubWebhook(ctx.request.originalRequest.request);
console.log(status);
ctx.response.status = 200;
});
const app = new Application()
.use(webhookLogger.routes())
.use(webhookLogger.allowedMethods());
// The port is not important in Deno Deploy
await app.listen({ port: 8080 });
I want to load a specific configuration (opening hours, company address,…) based on the subdomain. Thus I want to run a single instance of NextJS for different clients (each client has a subdomain).
I try
export async function getStaticProps() {
const subdomain = /:\/\/([^\/?]+)/.exec(window.location.href)[1].split(".")[0];
const config = mysqlquery(subdomain);
return {
props: {
config,
}
}
}
and I get window is not defined.
First install absoluteUrl then you should request to your local api to find subdomain let see the code:
export async function getStaticProps() {
const req = await
fetch("http://localhost:3000/api/hello");
const dd = await req.json();
console.log("sub", dd);
return {
props: {
aboutData: [],
},
};
}
now in pages/api/hello.js
import absoluteUrl from "next-absolute-url";
export default async function
handler(req,res) {
const { origin } = absoluteUrl(req);
let subdomain = origin.match(/\w+/);
console.log("request ", subdomain);
return res.status(200).json({ subDomain:
subdomain });
}
it will send an array subdomain is the first index of array actually i am not sure about regex that i write you can check if not work write your own regex to determin the subdomain
You can access the url using context.req.headers.referer, being your code as follows:
export async function getStaticProps(context) {
const subdomain = /:\/\/([^\/?]+)/.exec(context.req.headers.referer).split(".")[0];
const config = mysqlquery(subdomain);
return {
props: {
config,
}
}
}
Probably this is a very stupid question, i'm new in Node.js and javascript, so please forgive me if the question is not properly explained or the answer is simple...
I'm trying to send 2 variables thru a url... When i send only 1 variable (artist=${term}) work all good, but I'm really stuck with about how to send 2 variables thru the url (&artist=${term1}&album=${term2})
I've work on this code so far which for 1 variable is working well... but i have no idea how to add a second or a third variable to the request:
File 1: "./services/albumInfo.js"
import { BRV_API } from '../../config';
import axios from 'axios';
import dotenv from 'dotenv';
const ALBUM_INFO = 'method=album.getinfo';
dotenv.config();
const doRequest = async (url) => {
return await axios.get(`${BRV_API}/${url}&api_key=${process.env.API_KEY}&format=json`);
};
export const infoAlbum = async (term) => {
return await doRequest(`?${ALBUM_INFO}&artist=${term}`);
};
File 2: "./repositories/albumInfo.js"
import { infoAlbum } from '../repositories/albumInfo';
import status from 'http-status';
export const albumInfo = async (req, res, next) => {
try {
const { query } = req;
const { data } = await infoAlbum(query.name);
const response = data;
res.status(status.OK).send(response);
} catch (error) {
next(error);
}
};
I know that my problem is in this part of the code (I guess)
export const infoAlbum = async (term) => {
return await doRequest(`?${ALBUM_INFO}&artist=${term1}&album=${term2}`);
};
I've been searching, and i've seen some solution, like this one, but i just don't understand those solutions or how to apply on my code (sorry for that, im a very new on this)
Any good soul who can help this newbie? (if can explain the why of the solution as well, for understand, will be amazing!!)
Thanks in advance!!
Axios provides parameters that can be added custom as the following
const your_url = process.env.URL
const infoAlbum = await axios.get(your_url,{
params: {
artist: term,
album: term2,
api_key: process.env.API_KEY,
format:'json'
}
})
console.log(infoAlbum.data.args)
note: your_url without any more parameters.
So,
I've found a solution, which is pretty ugly, but so far is working, if someone have a better option, will be amazing to know:
File 1: repositories/albumInfo.js, I've just add the console (as per #Alex028502 suggestion), to know what the code was returning:
import { BRV_API } from '../../config';
import axios from 'axios';
import dotenv from 'dotenv';
const ALBUM_INFO = 'method=album.getinfo';
dotenv.config();
const doRequest = async (url) => {
const fullurl = `${BRV_API}/?${ALBUM_INFO}${url}&api_key=${process.env.API_KEY}&format=json`;
console.log('full url is', fullurl);
return await axios.get(fullurl);
};
export const infoAlbum = async (term) => {
return await doRequest(`&${term}`);
};
File 1: services/albumInfo.js: I change the behaviour of 'infoAlbum' to make the request from his side:
import { infoAlbum } from '../repositories/albumInfo';
import status from 'http-status';
export const albumInfo = async (req, res, next) => {
try {
const { query } = req;
console.log(query);
const { data } = await infoAlbum('artist=' + query.artist + '&album=' + query.album);
const response = data;
res.status(status.OK).send(response);
} catch (error) {
next(error);
}
};
I know that probably this is not the very best way to walk away from the problem, but so far is what i have.... any other better option about how to capture the second or third parameter of the url request and then add them to the final url?
Best!
All the endpoints in the backend require Authorization header. This header is stored in SecureStore.
Problem Statement
I want to load the Authorization header ( JWT Token ), for every API call after logging in.
Now this requires an async operation i.e.authStorage.getToken.
This is my client.js ( the apisauce client ).
client.js
import { create } from "apisauce";
import authStorage from "../auth/storage";
import IP from "../config/network";
const restoreToken = async () => {
return await authStorage.getToken("idToken");
};
const apiClient = (auth_token = "") =>
create({
baseURL: "http://" + IP + ":8990",
headers: { Authorization: auth_token }, // This I've added later
});
export default apiClient;
This is the PostsApi which uses apiClient to make the calls.
PostsApi.js
import apiClient from "./client";
const endpoint = "/api/";
const bookmarkEndpoint = "/bookmark/";
const getPosts = (last_id = 0, limit = 10) => {
return apiClient.get(endpoint + "?last_id=" + last_id + "&limit=" + limit);
};
const toggleBookmark = (item_id) => {
return apiClient.get(bookmarkEndpoint + "?item_id=" + item_id);
};
export default {
getPosts,
toggleBookmark,
};
My Understanding
I understand that if I can add the header in client.js itself, it would be injected everytime there's an API call.
I've tried :
const restoreToken = async () => {
return await authStorage.getToken("idToken");
};
But I am not sure how to call this async operation in client.js
Bonus Question
This token ( idToken ) would be reloaded every hour, so it's best to get the token from SecureStore everytime instead of saving it once.
Thanks.
Accepted answer and what worked for me
Worked for me
apisauce's setHeader : Documented here
Accepted answer is a detailed drilling of the axios setting up of headers. So if someone's using axios client directly they can see the accepted answer else, if you're an apisauce user, use the setHeader functionality provided with the library.
Cheers.
You will have to store your token with the state (can be redux or local state).
During save/refresh/reload the token, you will have set headers of the HTTP client.
You can set header using below command (example)
export const setAuthToken = (token) => {
apiClient.defaults.headers.common['Authorization'] = ''
delete apiClient.defaults.headers.common['Authorization']
if (token) {
apiClient.defaults.headers.common['Authorization'] = `Bearer ${token}`
}
}
Call the above function to set a token during reload/refresh/creation of token.
const restoreToken = async () => {
return await authStorage.getToken("idToken").then(token => setAuthToken(token));
};