How to access custom response values from a page script? - next.js

This might sound like a silly question but I have really tried everything I could to figure it out. I am creating a variable and adding it to my response object in custom Express server file like so:
server.get('*', (req, res) => {
res.locals.user = req.user || null;
handle(req, res);
});
Now I want to access this res.locals.user object from all of my pages, i.e. index.js, about.js, etc., in order to keep a tab on the active session's user credentials. It's got to be possible some way, right?
P.S.: Reading some thread on the NextJS Github page, I tried accessing it from my props object as this.props.user but it keeps returning null even when a server-side console.log shows non-null values.

The res object is available on the server as a parameter to getInitialProps. So, with the server code you have above, you can do
static async getInitialProps({res}) {
return { user: res.locals.user }
}
to make it available as this.props.user.

Related

Handle Firebase client-side token and access to protected pages

I'm using Firebase auth to login with Facebook, Google and email/pass. Basically, everything runs client-side, I make a call to Firebase and I receive an object containing an access token (that is a JWT ID Token), a customer id and its email. When I get this object, I put it into a persistent store (local storage, I know it's bad) and I perform an API call to one of my sveltekit endpoint that will in turn make another API call to a backend API (written in Go) to get all the user informations: firstname, lastname, phone, address, stats etc. To give a little bit of context, below is a diagram to illustrate what's happening when a user sign-in using Facebook.
Up to now, I just put the Firebase object into a store and just check if the information are there to allow access to a particular page. This check is done in the +layout.svelte page of the directory containing the page to be protected. It looks like something like this:
onMount(() => {
// redirect if not authenticated
if (browser && !$authStore?.uid) goto(`/auth/sign-in`);
});
It's probably not a good thing especially since my store persists in the local storage and therefore is prone to some javascript manipulation.
From my understanding, there's at least 2 things that could be better implemented but I may be wrong:
Set the access token in an httponly cookie straight after receiving it from Firebase. This would avoid storing the access token (that is a JWT) in the local storage and It could be used to authorize access or not to some protected pages...
I receive the Firebase authentication object on client-side buthttp only cookie can only be created from server side. I thought about sending the object as a payload to a POST sveltekit endpoint (like /routes/api/auth/token/+server.js) and set the cookie from here but apparently cookies is not available in a sveltekit endpoint.
So, how and where should I set this cookie ? I see that cookies is available in the load function of a +layout.server.js file, as well as in the handle function of a hooks.server.js file, but I don't see the logic here.
Populate locals.userwith the authenticated user once I've performed a call to my backend. Well, here, it's not obvious to me because I think point 1) would be enough to manage access to protected pages, but I see that a check of locals.user is something I've seen elsewhere.
I tried to set locals.user in the sveltekit endpoint that is making the API call to the backend API:
// /routes/api/users/[uid]/+server.js
import { json } from "#sveltejs/kit";
import axios from "axios";
import { GO_API_GATEWAY_BASE_URL } from "$env/static/private";
export async function GET({ request, locals, params }) {
try {
const config = {
method: "get",
baseURL: GO_API_GATEWAY_BASE_URL,
url: `/users/${params.uid}`,
headers: {
Authorization: `Bearer ${uidToken}`, // <-- the Firebase ID Token
},
withCredentials: true,
};
const res = await axios(config);
// set locals
locals.user = json(res.data); // <--- DOESN'T SEEM TO WORK
return json(res.data);
} catch (error) {...}
}
...but in the +layout.server.js page that I've created I see nothing:
// routes/app/protected_pages/+layout.server.js
import { redirect } from "#sveltejs/kit";
export function load({ locals }) {
console.log(locals); // <----- contains an empty object: {}
if (!locals.user) throw redirect(302, "/auth/sign-in");
}
Thank you so much for your help

Cloudflare Workers - changes are not visible on live (but are in preview)

Hello and thank you for your help.
Sadly support over at CF does not think they need to help me.
I am learning to use workers, and have written a simple HTML injector just to see it working on my site.
this is the full worker code i have:
async function handleRequest(req) {
const res = await fetch(req)
const contentType = res.headers.get("Content-Type")
console.log('contentType: ', contentType)
// If the response is HTML, it can be transformed with
// HTMLRewriter -- otherwise, it should pass through
if (contentType.startsWith("text/html")) {
return rewriter.transform(res)
} else {
return res
}
}
class UserElementHandler {
async element(element) {
element.before("<div class='contbox'><img src='https://coverme.co.il/wp-content/uploads/2020/01/covermeLOGO-01-1024x183.png' style='width:200px;margin:20px;'><h1>testing inserting</h1></div>", {html: true});
// fill in user info using response
}
}
const rewriter = new HTMLRewriter()
.on("h1", new UserElementHandler())
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
it just uses element.before to inject some HTML.
in the worker preview pane i can see it!
but on the live site = nothing.
this is the active URL: [https://coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/]
these are the 4 routes i have set up to try to catch this, with and without encoding the letters:
coverme.co.il/product/נר-בינוני-tuberosejasmine/
*.coverme.co.il/product/נר-בינוני-tuberosejasmine/*
https://coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/
*.coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/*
thanks in advance!
I believe the problem here is that you've configured your routes to match "נר-בינוני" unescaped, but the browser will actually percent-encode the URL before sending to the server, therefore the route matching actually operates on percent-escaped URLs. So the actual URL is https://coverme.co.il/product/%D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99-tuberosejasmine/, and this does not match your route because %D7%A0%D7%A8-%D7%91%D7%99%D7%A0%D7%95%D7%A0%D7%99 is not considered to be the same as נר-בינוני.
EDIT: Unfortunately, using percent-encoding in your route pattern won't fix the problem, due to a known bug. Unfortunately, it's just not possible to match non-ASCII characters in a Workers route today. We intend to fix this, but it's hard because some sites are accidentally dependent on the broken behavior, so the fix would break them.
What you can potentially do instead is match against coverme.co.il/product/*, and then, inside your worker, check if the path also has נר-בינוני-tuberosejasmine. If it does not, then your fetch event handler should simply return without calling event.respondWith(). This will trigger "default handling" of the request, meaning it will pass through and be sent to your origin server like normal. (Note that you will still be billed for a Workers request, though.)
So, something like this:
addEventListener("fetch", event => {
if (event.request.url.includes(
"coverme.co.il/product/נר-בינוני-tuberosejasmine/")) {
event.respondWith(handle(event.request));
} else {
return; // not a match, use default pass-through handling
}
})

Is it possible to stub meteor methods and publications in cypress tests?

Is it possible to stub meteor methods and publications in cypress tests?
In the docs (https://docs.cypress.io/guides/getting-started/testing-your-app#Stubbing-the-server) it says:
This means that instead of resetting the database, or seeding it with
the state we want, you can force the server to respond with whatever
you want it to. (...) and even test all of the edge cases, without needing a server.
But I do not find more details about that. All I can find is, that when not using the virtual user in the tests to fill the database, it is possible to call API calls on the app, like so:
cy.request('POST', '/test/seed/user', { username: 'jane.lane' })
.its('body')
.as('currentUser')
In my opinion that is not a "stub". It is a method to "seed" the database.
Is it possible to tell cypress to answer a meteor method in the client code like
Meteor.call('getUser', { username: 'jane.lane' }, callbackFunction);
with some data, it would give back in production?
I can only show an example using sinon to stub Meteor method calls:
const stub = sinon.stub(Meteor, 'call')
stub.callsFake(function (name, obj, callback) {
if (name === 'getUser' && obj.username === 'jane.lane') {
setTimeout(function () {
callback(/* your fake data here */)
})
}
})
That would be of corse a client-side stub. You could also simply override your Meteor method for this one test.

Apolllo client-state preset cache does not work on server side render

I am trying to use Apollo client-state data with the hooks useQuery and useMutation as a global store inside a NextJS app.
When I follow the example from NextJS called with-apollo (https://github.com/zeit/next.js/tree/canary/examples/with-apollo), which uses a HOC in which it runs all the queries and renders the app on the server first.
Normally, you want to catch the 'loading' state in a component like this:
const { loading, data } = useQuery(GET_NAME_QUERY) // some #client query
if (loading) return <p>Loading...</p>
return <h1>Hello {data.name</h1>
But I want to skip the loading-check and always have a default state available. I think this could be done with setting the cache of the apollo-client to a set of default values, like this:
const cache = new InMemoryCache().restore(initialState);
// This is the part where the initial client-state data is set:
cache.writeData({
data: { name: 'Harry' }
});
const apolloClient = new ApolloClient({
cache, // <-- cache with preset client-state data
typeDefs,
resolvers,
})
With the goal to just do this:
const { data } = useQuery(GET_NAME_QUERY) // some #client query, resolves immediately from cache
return <h1>Hello {data.name</h1>
I created a repo with a robot and its name & status that are cached from the beginning this way. See https://github.com/rnacken/next-with-apollo-client-state-cache
The client works, without any errors, but during SSR, I get an error, since it does not have the data yet (and 'loading' is true) after a useQuery call.
When I log the loading/data state, I see this on the server:
On the client, it's all good:
Does anybody know how this is happening? With the cache set, I shouldn't see a loading:true state ever right? Is the getDataFromTree from #apollo/react-ssr not working correctly?

Post request redux thunk

I have GET requests and normally when those succeeded I save data in store, but for POST requests I need to know if it succeeded or not, in order to execute some code (show a message and redirect), the docu says you can use an isLoading variable, but it just says if the service is working but not if it succeeded, if I try to create a new success variable in the store, it will be turned on forever after the request and I don't need that either. I tried returning a promise from the action creator and handle response directly inside the component but it looks like the same to call axios there instead of using redux.
My action creator looks like this:
export function createProject(userId, projectName) {
return function (dispatch) {
dispatch({ type: projectsActions.START_CREATE_PROJECT });
return ProjectsService.createProject(userId, projectName).then(() => {
dispatch({ type: projectsActions.SUCCESS_CREATE_PROJECT });
}).catch((error) => {
dispatch({ type: projectsActions.ERROR_CREATE_PROJECT });
throw error;
});
}
}
I understand where your doubts are coming from, it doesn't seem appropriate to have a field on your Redux store only to know the success of a one-time request.
If you only need to make a post request and only care about it's result once, the simplest way to do it is to use state in the component making the request. Component-level state is easily manageable and gets removed from memory when the component is unmounted, but on the other hand you may want to have a single source of truth for your app. You have to make a choice, but your Redux implementation is correct.

Resources