I am trying to customize my server using Next.js:
const express = require('express')
const next = require('next')
port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({dev, dir: '../'})
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/a', (req, res) => {
res.send("Palin text: Hello From Express server Riko :)")
})
server.get('/b', (req, res) => {
return app.render(req, res, '/b', req.query)
})
server.get('/posts/:id', (req, res) => {
return app.render(req, res, '/posts', { id: req.params.id })
})
server.get('*', (req, res) => {
res.removeHeader('Transfer-Encoding')
res.setHeader('Connection', 'close')
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
When a page is being loaded i see that the response header Connection is set to 'keep-alive'. And this results in very long response time.
I tried to change the header here:
server.get('*', (req, res) => {
res.removeHeader('Transfer-Encoding')
res.setHeader('Connection', 'close')
return handle(req, res)
})
Without success though.
This is the Next.js configuration file:
module.exports = {
distDir: 'build',
poweredByHeader: false
}
use this:
res.set('Connection', 'close');
Related
I'm trying to test my nextjs app with Jest but according to this example , I have to tell Axios to use node adapter while running in testing mode but for mome reason I'm getting this error.
jest.setup.js
import '#testing-library/jest-dom/extend-expect'
import axios from 'axios';
axios.defaults.adapter = require('axios/lib/adapters/http');
index.test.js
const mockedUseRoomsQuery = useRoomsQuery;
jest.mock("../__mocks__/roomMockData.js");
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
describe("Fetch all rooms", () => {
it("fetches all rooms", async () => {
nock("https://63b29d465901da0ab368e025.mockapi.io", {
reqheaders: { "app-id": () => true },
})
.persist()
.get("/api/v1/rooms")
.reply(200, roomData);
const { result, waitFor } = renderHook(() => useRoomsQuery(), { wrapper });
await waitFor(() => result.current.isSuccess);
console.log("data returned", result.current);
}, 10000);
});
What am I doing wrong?
I have the multilanguege app in next.js with ISR. Data fetch using graphQL. The API return data for a given language after getting origin from header request. Unfortunately, If I do it in function getStaticProps() then origin is empty. How Can I submit origin which domain is request?
In example below use apollo client.
The origin header is not visible in the API.
Where should I type something?
How can I submit this origin?
export const APOLLO_STATE_PROP_NAME = '__APOLLO_STATE__'
let apolloClient: ApolloClient<NormalizedCacheObject> | undefined
const createApolloClient = (headers: IncomingHttpHeaders | null = null) => {
const enhancedFetch = (url: RequestInfo, init: RequestInit) => {
return fetch(url, {
...init,
headers: {
...init.headers,
Cookie: headers?.cookie ?? '',
},
}).then((response) => {
return response
})
}
return new ApolloClient({
ssrMode: typeof window === 'undefined',
link: ApolloLink.from([
onError(({ graphQLErrors, networkError, operation, forward, response }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path, }) =>
console.error(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
)
}
if (networkError)
console.error(
`[Network error]: ${networkError}. Backend is unreachable. Is it running?`
)
}),
createUploadLink({
uri: "https://api.url_site/api/graphql",
fetchOptions: {
mode: 'cors',
},
credentials: 'same-origin',
fetch: enhancedFetch,
}),
]),
cache: new InMemoryCache(),
})
type InitialState = NormalizedCacheObject | undefined
interface IInitializeApollo {
headers?: IncomingHttpHeaders | null
initialState?: InitialState | null
}
export const initializeApollo = (
{ headers, initialState }: IInitializeApollo = {
headers: {
// 'Access-Control-Allow-Origin': '*'
},
initialState: null,
}
) => {
const _apolloClient = apolloClient ?? createApolloClient(headers)
if (initialState) {
const existingCache = _apolloClient.extract()
const data = merge(existingCache, initialState, {
arrayMerge: (destinationArray, sourceArray) => [
...sourceArray,
...destinationArray.filter((d) =>
sourceArray.every((s) => !isEqual(d, s))
),
],
})
_apolloClient.cache.restore(data)
}
if (typeof window === 'undefined') return _apolloClient
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
export function addApolloState(
client: ApolloClient<NormalizedCacheObject>,
pageProps: AppProps['pageProps']
) {
// #ts-ignore
if (pageProps?.props) {
// #ts-ignore
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract()
}
return pageProps
}
export function useApollo(pageProps: AppProps['pageProps']) {
// #ts-ignore
const state = pageProps[APOLLO_STATE_PROP_NAME]
const store = useMemo(() => initializeApollo(state), [state])
return store
}
}
I'm trying to deploy areact-app using express server but I've got a problem while trying to link the front and the back. The jsx pages are working, but the style is not.
Here is my server.js document:
const express = require("express");
const cors = require("cors");
const path = require("path");
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
require("dotenv").config({ path: "./config/.env" });
require("./config/db");
const { checkUser, requireAuth } = require("./middleware/auth.middleware");
const userRoutes = require("./routes/user.routes");
const messageRoutes = require("./routes/message.routes");
const app = express();
app.use(express.static(path.join(__dirname, "client/build")));
const corsOptions = {
origin: process.env.CLIENT_URL,
credentials: true,
allowedHeaders: ["sessionId", "Content-Type"],
exposedHeaders: ["sessionId"],
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
preflightContinue: false,
};
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.json());
app.use(cookieParser());
app.use(cors(corsOptions));
// jwt
app.get("*", checkUser);
app.get("/jwtid", requireAuth, (req, res) => {
res.status(200).send(res.locals.user._id);
});
app.use("/api/user", userRoutes);
app.use("/api/message", messageRoutes);
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname + "/client/build/index.html"));
});
app.listen(process.env.PORT, () => {
console.log(`listening on port ${process.env.PORT}`);
});
and here 's my file organisation
I am new to RTK query and need only one WebSocket connection for my entire application as you can see below I implemented it like an example in GitHub.
I need to somehow send my payload to this WebSocket by subscribing to it.
and then whenever the message comes in I update the other injected Endpoints' cache.
import { ApiSlice } from 'api';
import { instrumentsAdapter } from './marketSlice';
const socket = new WebSocket(process.env.REACT_APP_SOCKET_BASE_URL);
const socketConnected = new Promise((resolve, reject) => {
// Connection opened
try {
socket.addEventListener('open', (event) => {
resolve(event);
});
} catch (err) {
console.log('err', err);
reject(err);
}
});
export const socketApi = ApiSlice.injectEndpoints({
endpoints: (builder) => ({
socketChannel: builder.mutation({
async queryFn(arg) {
await socketConnected;
const { type, topic } = arg;
const sendPayload = { type, path: topic };
socket.send(JSON.stringify(sendPayload));
return { data: { messages: [] } };
},
async onCacheEntryAdded(arg, { cacheDataLoaded, cacheEntryRemoved }) {
console.log('arg', arg);
await cacheDataLoaded;
// Listen for messages
socket.onmessage = (res) => {
const message = JSON.parse(res.data);
try {
// ApiSlice.util.updateQueryData('getInstrumentByRefId', arg, (draft) => {
// console.log('arg', arg);
// draft = { ...message.value, baseVolume: 3 };
// });
} catch (err) {
console.log('err', err);
}
};
await cacheEntryRemoved;
socket.close();
}
})
})
});
export const { useSocketChannelMutation } = socketApi;
after so much reading docs and researching I finally find this solution working but I do not know if this is a best practice or not.
Here is my not-empty ApiSlice.
/* eslint-disable import/prefer-default-export */
// Or from '#reduxjs/toolkit/query' if not using the auto-generated hooks
import { createApi } from '#reduxjs/toolkit/query/react';
import axiosBaseQuery from './axiosBaseQuery';
export const socket = new WebSocket(process.env.REACT_APP_SOCKET_BASE_URL);
const socketConnected = new Promise((resolve, reject) => {
try {
socket.addEventListener('open', (event) => {
resolve(event);
});
} catch (err) {
reject(err);
}
});
// initialize an empty api service that we'll inject endpoints into later as needed
export const ApiSlice = createApi({
reducerPath: 'api',
baseQuery: axiosBaseQuery(),
endpoints: (builder) => ({
subscribeSocket: builder.mutation({
async queryFn(arg) {
await socketConnected;
const sendPayload = { type: 'SUBSCRIBE', path: arg };
socket.send(JSON.stringify(sendPayload));
return { data: { messages: [] } };
}
}),
unsubscribeSocket: builder.mutation({
async queryFn(arg) {
await socketConnected;
const sendPayload = { type: 'UNSUBSCRIBE', path: arg };
socket.send(JSON.stringify(sendPayload));
return { data: { messages: [] } };
}
}),
channel: builder.mutation({
async queryFn(onMessage) {
await socketConnected;
socket.addEventListener('message', onMessage);
return { data: { messages: [] } };
}
})
})
});
export const { useUnsubscribeSocketMutation, useSubscribeSocketMutation, useChannelMutation } =
ApiSlice;
and this is my enhanced Api slice
import { createEntityAdapter } from '#reduxjs/toolkit';
import { ApiSlice } from 'api';
export const instrumentsAdapter = createEntityAdapter({
selectId: (item) => item?.state?.symbol
});
export const marketApi = ApiSlice.injectEndpoints({
overrideExisting: false,
endpoints: (builder) => ({
getMarketMap: builder.query({
query: (type) => ({
url: `/market/map?type=${type}`,
method: 'get'
})
}),
getInstruments: builder.query({
query: (type) => ({
url: `/market/instruments?type=${type}`,
method: 'get'
})
}),
getInstrumentByRefId: builder.query({
query: (refId) => ({
url: `/market/instruments/${refId}/summary`,
method: 'get'
}),
transformResponse: (res) => {
return instrumentsAdapter.addOne(instrumentsAdapter.getInitialState(), res);
},
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved, dispatch }
) {
await cacheDataLoaded;
const payload = `instruments.${arg}.summary`;
// subs to socket
dispatch(ApiSlice.endpoints.subscribeSocket.initiate(payload));
// Listen for messages
const onMessage = (res) => {
const message = JSON.parse(res.data);
try {
updateCachedData((draft) => {
instrumentsAdapter.setOne(draft, message.value);
});
} catch (err) {
// eslint-disable-next-line no-console
console.log('err', err);
}
};
dispatch(ApiSlice.endpoints.channel.initiate(onMessage));
await cacheEntryRemoved;
// unsubs to socket
dispatch(ApiSlice.endpoints.unsubscribeSocket.initiate(payload));
}
}),
getCandles: builder.query({
query: ({ refId, bucket, end, limit = 1 }) => ({
url: `/market/instruments/${refId}/candles?bucket=${bucket}&end=${end}&limit=${limit}`,
method: 'get'
})
})
})
});
export const {
useGetMarketMapQuery,
useGetInstrumentByRefIdQuery,
useGetInstrumentsQuery,
useGetCandlesQuery
} = marketApi;
and I try to dispatch my socket endpoints from ApiSlice inside of onCacheEntryAdded.
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved, dispatch }
) {
await cacheDataLoaded;
const payload = `instruments.${arg}.summary`;
// subs to socket
dispatch(ApiSlice.endpoints.subscribeSocket.initiate(payload));
// Listen for messages
const onMessage = (res) => {
const message = JSON.parse(res.data);
try {
updateCachedData((draft) => {
instrumentsAdapter.setOne(draft, message.value);
});
} catch (err) {
// eslint-disable-next-line no-console
console.log('err', err);
}
};
dispatch(ApiSlice.endpoints.channel.initiate(onMessage));
await cacheEntryRemoved;
// unsubs to socket
dispatch(ApiSlice.endpoints.unsubscribeSocket.initiate(payload));
}
}),
```
I am trying to test my redux action creator.
When axios version was 0.19 it was working fine. But when upgraded to version 0.24.0
it returns empty array.
React version : 16.12.0
here is my code :
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Actions', () => {
let store;
let server;
beforeEach(() => {
store = mockStore();
store.clearActions();
server = fakeServer.create({ respondImmediately: true });
});
afterEach(() => {
server.restore();
});
describe('for My api', () => {
it('should dispatch successful _RESULTS_REQUEST_FULFILLED action', (done) => {
const payload = JSON.stringify(myMock);
const expectedAction = [
{
type: actionConstant.MY_RESULTS_REQUEST_FULFILLED,
payload: myMock,
},
];
server.respondWith('GET', 'https://' + API_HOST_NAME + '/emp/api?&q=&start=0&sort=', [
200,
{ 'Content-Type': 'application/json' },
payload,
]);
setImmediate(() => {
store
.dispatch(actionCreators.getResults())
.then(() => {
expect(store.getActions()).toEqual(expectedAction);
})
.catch((err) => {
console.log(err);
});
done();
});
});