Uploading Stream to Aws S3 not working Properly - javascript

I am trying to Upload stream to AWS s3. now my File Manager can upload stream of objects on server without any load or bloating. But when it comes to AWS s3 it returns successful response and even AWS SDK is not reporting anything about misconfiugration it also returns Successful Upload so my File Manager is fine But. Problem is that when i take a look on AWS Console i only see path of given file not file itself..For better Understanding Please take a look at following code and Images.
Stream to Upload (File)
ReadStream {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 65536,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: null,
pipesCount: 0,
flowing: null,
ended: false,
endEmitted: false,
reading: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
emitClose: false,
autoDestroy: false,
destroyed: false,
defaultEncoding: 'utf8',
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
readable: true,
_events: [Object: null prototype] {
end: [ [Function], [Function: remove] ],
error: [Function],
close: [Function: remove]
},
_eventsCount: 3,
_maxListeners: undefined,
path: '/tmp/capacitor-7957c2e3611868508c7ac8ec64c1a7c2.tmp',
fd: null,
flags: 'r',
mode: 438,
start: undefined,
end: Infinity,
autoClose: true,
pos: undefined,
bytesRead: 0,
closed: false,
name: undefined,
_writeStream: WriteStream {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: true,
needDrain: false,
ending: true,
ended: true,
finished: true,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: true,
errorEmitted: false,
emitClose: false,
autoDestroy: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object]
},
writable: false,
_events: [Object: null prototype] { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
path: '/tmp/capacitor-7957c2e3611868508c7ac8ec64c1a7c2.tmp',
fd: 30,
flags: 'w',
mode: 438,
start: undefined,
autoClose: false,
pos: undefined,
bytesWritten: 46355,
closed: false,
_readStreams: Set { [Circular] },
error: null,
_cleanupSync: [Function],
[Symbol(kCapture)]: false,
[Symbol(kIsPerformingIO)]: false
},
error: null,
[Symbol(kCapture)]: false,
[Symbol(kIsPerformingIO)]: false
}
FileManager AWS S3 Code
if (this.configuration.Aws && this.configuration.Aws.s3 && this.configuration.Aws.s3.accessKey && this.configuration.Aws.s3.secretKey) {
// Local variable.
let _Aws, _StreamUpload
// Upload file to s3
_Aws = new Aws.S3({
'accessKeyId': this.configuration.Aws.s3.accessKey,
'secretAccessKey': this.configuration.Aws.s3.secretKey
})
/*
* Only successful upload
* reply to client else report
* failure.
*/
if (!_.isEmpty(_Aws) && !(_Aws instanceof Error)) {
console.log('erw---rer>>', file)
/*
* Steam upload to aws.
* upload file to aws.
*/
_StreamUpload = await new Promise((__resolve, __reject) =>
// Upload file stream to aws s3
_Aws.upload({
'Bucket': this.configuration.Aws.s3.bucketName,
'Key': path,
'Body': file
}, (error, __data) => error instanceof Error ? __reject(error) : __resolve(__data)))
/*
* Only return _StreamUpload if
* uploading didnt contain any error.
*/
if (_StreamUpload && !(_StreamUpload instanceof Error)) {
// Return upload.
return { 'storageType': 's3', 'path': _StreamUpload.Location }
}
// Report failure.
return _StreamUpload
}
// Report failure.
return _.isEmpty(_Aws) ? new Error('UPLOAD_FAILED(AWS)') : _Aws
}
AWS S3 Response
{
ETag: '"1ea966ff33714024a163d51e06b9bad8"',
Location: 'https://truckpe.s3.amazonaws.com//ck7ef9esk000k0758t9r3mngq/1585455100505/',
key: '/ck7ef9esk000k0758t9r3mngq/1585455100505/',
Key: '/ck7ef9esk000k0758t9r3mngq/1585455100505/',
Bucket: 'truckpe'
}
AWS S3 Console Screen Shot

Related

NextJS not updating data from getStaticProps

I have a NextJS web app and NestJS backend. Locally with npm run dev or npm run build && npm start all works fine.
But deployed app (front and back in docker on different servers) not updating data on groups page.
When I build my app it generates a page with data from backend. Then I can add a new group (just new data), return to the group's page and can not see my mewGroup in the list.
My getStaticProps (not working):
export const getStaticProps: GetStaticProps<GroupProps> = async () => {
const { getGroups, getUsers, getModules } = useHttp();
const { data: groups } = await getGroups();
const { data: users } = await getUsers();
const { data: modules } = await getModules();
return {
props: { groups, users, modules },
revalidate: 5
};
};
My post request (working):
createGroup(body)
.then(() => {
push(routes.groups);
})
.catch((error) => {
throwError && throwError(error);
});
Again, it works fine locally even with the production backend.
But production web does not update any info, just can push some.
It's my first NextJS app, but I worked with react.
In my production logs I see the error, but have no idea how to handle it:
Error: getaddrinfo EAI_AGAIN {{MY_BACK_SERVER}}
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:71:26) {
errno: -3001,
code: 'EAI_AGAIN',
syscall: 'getaddrinfo',
hostname: '{{MY_BACK_SERVER}}',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.24.0'
},
method: 'get',
url: 'http://{{MY_BACK_SERVER}}:5000/groups',
data: undefined
},
request: <ref *1> Writable {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
errored: null,
closed: false
},
_events: [Object: null prototype] {
response: [Function: handleResponse],
error: [Function: handleRequestError]
},
_eventsCount: 2,
_maxListeners: undefined,
_options: {
maxRedirects: 21,
maxBodyLength: 10485760,
protocol: 'http:',
path: '/groups',
method: 'GET',
headers: [Object],
agent: undefined,
agents: [Object],
auth: undefined,
hostname: '{{MY_BACK_SERVER}}',
port: '5000',
nativeProtocols: [Object],
pathname: '/groups'
},
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
_header: 'GET /groups HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/0.24.0\r\n' +
'Host: {{MY_BACK_SERVER}}:5000\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/groups',
_ended: false,
res: null,
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: '{{MY_BACK_SERVER}}',
protocol: 'http:',
_redirectable: [Circular *1],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
_currentUrl: 'http://{{MY_BACK_SERVER}}:5000/groups',
[Symbol(kCapture)]: false
},
response: undefined,
isAxiosError: true,
toJSON: [Function: toJSON]
}
so when you first open the group page it showed you the information from the static file,
there are two cases -
now if there is a modification in the data (ie befor opening the group page for the first time) it will show you the old data (as it works with stale
while revalidate cache) and update the data (in background) for group page
(getStaticProps ). so when you again visit the group page again it
will show you the updated data.
Now the second case is (the one related to you) you opened the group page it checks there is no need to update the page in the
background as there is no new data in the server, it shows you the
data and then you in the same session updated the group info (added a
new data), next js will not show those newly created data because from
here it is serving you pages from the local cache. just because it
doesn't see any update of data in the first render.
You will have to do a full refresh to see the newly created data.
recommendation - use full page refresh occasionally (programmatically) when you think there may be a new data created or user server side rendering.
Thanks
Finally, I solve the problem. My container has no internet connection, SSR just can't ping the backend. POST request works well because it client-side request.

Axios/Nuxt "Certificate has expired" only when directly opening page, but not for a link from another page

I have a website I am helping to maintain (but did not build myself), using Nuxt.js and Axios. It is currently showing an odd server behavior on the Home page. If I directly visit the home page (say: https://example.org):
Browser: Server error (500)
Server: Certificate has expired (for the twitter feed data call)
This error also happens when I am on example.org/about (which works) and hit a link formed like: Home
However, if I visit the home page (both show link as https://example.org) via a link on say example.org/about which is formed like: <span>Home</span>:
Browser: Page works!
Server: No errors
So, oddly enough, even for two different links for '/' one leads to the error but the other does not (and they both display the same thing in the URL bar). If I open the link directly at example.org:5000/tweets then it works fine (footnote: not sure why the design choice was made to use a different port there, possibly related). Has anyone seen this kind of behavior and know why that might happen? It persists on safe mode and on all major browsers. No changes were made with the code from when it worked to when it stopped working in the last couple of days. The specific error that I receive is shown like the below.
{ Error: certificate has expired
at TLSSocket.<anonymous> (_tls_wrap.js:1105:38)
at emitNone (events.js:106:13)
at TLSSocket.emit (events.js:208:7)
at TLSSocket._finishInit (_tls_wrap.js:639:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:469:38)
code: 'CERT_HAS_EXPIRED',
config:
{ adapter: [Function: httpAdapter],
transformRequest: { '0': [Function: transformRequest] },
transformResponse: { '0': [Function: transformResponse] },
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers:
{ Accept: 'application/json, text/plain, */*',
'User-Agent': 'axios/0.16.2' },
baseURL: 'https://example.org:5000/',
method: 'get',
url: 'https://example.org:5000/tweets',
data: undefined },
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
domain: null,
_events:
{ response: [Function: handleResponse],
error: [Function: handleRequestError] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ protocol: 'https:',
maxRedirects: 21,
maxBodyLength: 10485760,
hostname: 'example.org',
port: '5000',
path: '/tweets',
method: 'get',
headers: [Object],
agent: undefined,
auth: undefined,
nativeProtocols: [Object],
pathname: '/tweets' },
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'GET /tweets HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nUser-Agent: axios/0.16.2\r\nHost: example.org:5000\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/tweets',
_ended: false,
res: null,
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(outHeadersKey)]: [Object] },
_currentUrl: 'https://example.org:5000/tweets' },
response: undefined,
statusCode: 500,
name: 'NuxtServerError' }
Upgrading to the latest version of Node resolved this issue for me. I was experiencing an identical issue to what you described on Node v8.10.0. I upgraded to Node v14.18.1 and restarted Nuxt, and now the site works normally.
sudo npm cache clean -f
sudo npm install -g n
sudo n stable

createReadStream is not present in uploaded image

I am using node v14.17.0,"apollo-server-express": "^2.25.0", "graphql-upload": "^12.0.0"
I am trying to upload an image but i dont get createReadStream from the image which i upload from graphiql. I am using Altair graphiql
Here is my index.js
const app = express();
app.use(
graphqlUploadExpress({
maxFileSize: 30000000,
maxFiles: 20,
})
);
const server = new ApolloServer({
typeDefs,
resolvers,
context: contextMiddleware,
uploads: false,
});
server.applyMiddleware({ app });
app.listen(PORT, () => {
console.log("Success");
});
In my typedefs i have mentioned
gql`
scalar Upload
and my resolvers have
Upload: GraphQLUpload,
I have tried following this issue apollographql/apollo-server#3508 but it doesn't seem to work for me. Downgrading to nodev12 did not help.
My uploaded image contains following
{"filename":"download123.jpg","mimetype":"image/jpeg","encoding":"7bit"}
Below is a single file upload example that uses the graphql-upload and apollo-server-express packages.
Package versions:
"apollo-server-express": "^2.15.1",
"graphql-upload": "^12.0.0",
"graphql": "^14.6.0",
Nodejs version: v14.16.0.
GraphQL server:
import { ApolloServer, gql } from 'apollo-server-express';
import express from 'express';
import path from 'path';
import { createWriteStream, unlink } from 'fs';
import { graphqlUploadExpress } from 'graphql-upload';
const app = express();
const PORT = 3000;
const UPLOAD_DIR = path.resolve(__dirname, './uploads');
const typeDefs = gql`
scalar Upload
type Query {
dummy: String
}
type Mutation {
upload(upload: Upload!): Boolean
}
`;
const resolvers = {
Query: {},
Mutation: {
async upload(_, { upload }) {
const { file } = upload;
const { filename, mimetype, createReadStream } = await file;
const stream = createReadStream();
const id = Date.now();
const uploadPath = `${UPLOAD_DIR}/${id}-${filename}`;
console.log(filename, mimetype, createReadStream, stream);
await new Promise((resolve, reject) => {
const writeStream = createWriteStream(uploadPath);
writeStream.on('finish', resolve);
writeStream.on('error', (error) => {
unlink(uploadPath, () => {
reject(error);
});
});
stream.on('error', (error) => writeStream.destroy(error));
stream.pipe(writeStream);
});
return true;
},
},
};
app.use(
graphqlUploadExpress({
maxFileSize: 30000000,
maxFiles: 20,
}),
);
const server = new ApolloServer({
typeDefs,
resolvers,
uploads: false,
});
server.applyMiddleware({ app });
app.listen(PORT, () => console.log(`GraphQL server is listening on http://localhost:${PORT}/graphql`));
Send client GraphQL request using Altair graphql.
The logs of the server side, get the createReadStream successfully.
GraphQL server is listening on http://localhost:3000/graphql
matej-sefcik-GCRbVZydPT4-unsplash.jpg image/jpeg [Function: createReadStream] <ref *1> ReadStream {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: null,
ended: false,
endEmitted: false,
reading: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: true,
autoDestroy: true,
destroyed: false,
errored: null,
closed: false,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
_events: [Object: null prototype] { close: [Function: remove] },
_eventsCount: 1,
_maxListeners: undefined,
_pos: 0,
_writeStream: WriteStream {
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: true,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 63781,
writing: true,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: [Function: nop],
writelen: 63781,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 1,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: false,
errored: null,
closed: false
},
_events: [Object: null prototype] {
error: [Array],
unpipe: [Function: onunpipe],
close: [Function],
finish: [Function],
ready: [Function],
drain: [Function: pipeOnDrainFunctionResult]
},
_eventsCount: 6,
_maxListeners: undefined,
_fd: null,
_path: null,
_pos: 0,
_readStreams: Set(1) { [Circular *1] },
_released: false,
_cleanupSync: [Function (anonymous)],
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false
}
Upload success:
source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/file-upload
I was able to resolve the error after upgrading to apollo-server v3. I think the v2 was somewhat conflicting with graphql-upload lib
const { filename, mimetype, createReadStream } = await file.promise;

Save raw response/binary data for a PDF response

I am getting back a response in node to fetch a PDF that I would like to save the binary data and upload it to S3 which I can then pull as I please for historical purposes. Right now I am having problems trying to extract that PDF raw data/binary data so that I can save that as a PDF and upload it, I think I am getting closer. this is the response.body that I am getting.
PassThrough {
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: null,
pipesCount: 0,
flowing: null,
ended: false,
endEmitted: false,
reading: false,
sync: false,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
emitClose: true,
autoDestroy: false,
destroyed: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
decoder: null,
encoding: null,
[Symbol(kPaused)]: null
},
readable: true,
_events: [Object: null prototype] {
prefinish: [Function: prefinish],
unpipe: [Function: onunpipe],
error: [ [Function: onerror], [Function] ],
close: [Function: bound onceWrapper] { listener: [Function: onclose] },
finish: [Function: bound onceWrapper] { listener: [Function: onfinish] }
},
_eventsCount: 5,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: false,
bufferedRequestCount: 0,
corkedRequestsFree: {
next: null,
entry: null,
finish: [Function: bound onCorkedFinish]
}
},
writable: true,
allowHalfOpen: true,
_transformState: {
afterTransform: [Function: bound afterTransform],
needTransform: false,
transforming: false,
writecb: null,
writechunk: null,
writeencoding: null
},
[Symbol(kCapture)]: false
}
This is my code:
platform.on(platform.events.loginSuccess, async function(e) {
let resp = await platform.get('/restapi/v1.0/account/~/extension/'+accountId+'/message-store', {
messageType: ['Fax'],
dateFrom: ['2021-05-01'],
dateTo: ['2021-05-16']
})
let jsonObj = await resp.json()
let attachId = jsonObj.records[0].id
let getMessageContent = await platform.get('/restapi/v1.0/account/~/extension/'+accountId+'/message-store/'+attachId+'/content/'+attachId, {
contentDisposition: ['Inline']
})
I know the response I am getting is correct because if I run that URL in postman and use the preview option, I can see the pdf (see screenshot). What am I missing?
That looks about right to me. The response.body is a readable node.js stream containing the binary data, and my guess is that Postman reads this stream automatically and displays the result.
The good news is that this type of stream is exactly what S3 wants as a request body when uploading files, which means that you do not need to save it anywhere - you can just pass it through directly to S3. Depending on your implementation, your code could look something like this:
const result = await s3Client.upload({
Bucket: 'MY_S3_BUCKET',
Key: 'myfilename.pdf',
Body: response.body,
}).promise();
Where response.body is the stream you have logged above. This should put the pdf file right up in S3 ready for downloading.
But if you do insist on saving the file, you can also pass the stream to the fs.writeFileSync method like so:
fs.writeFileSync('my-pdf.pdf', response.body);

Axios api proxy on nuxt doesn't work on server side rendering on now zeit deploy

Following the Axios NuxtJS config, I created a proxy config like this on my nuxt.config.js:
proxy: {
'/api/': {
target: 'https://myapidomain.com/',
pathRewrite: { '^/api/': '' },
changeOrigin: true
}
}
This config works perfectly on dev environment, both server side rendering and client side rendering. Here is the code we use to create an api wrapper api.js:
export default (context, inject) => {
inject('api', {
getPageForSlug: (slugRoute) => {
return context.$axios.$get(`/api/pageForSlug?routeName=${slugRoute}`)
},
})
}
and then from any vue class:
const response = await app.$api.getPageForSlug(params.slug_route)
the only problem occurs when this code is called from the zeit now deployment. The client side works perfectly, but the server side returns this error:
START RequestId: dd92dbad-135f-414b-bc1d-df9faffaa681 Version: $LATEST
2019-08-30T17:46:03.098Z dd92dbad-135f-414b-bc1d-df9faffaa681 INFO λ Cold start took: 5265.617811ms
END RequestId: dd92dbad-135f-414b-bc1d-df9faffaa681
REPORT RequestId: dd92dbad-135f-414b-bc1d-df9faffaa681 Duration: 438.53 ms Billed Duration: 500 ms Memory Size: 3008 MB Max Memory Used: 92 MB
N',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
data: undefined },
request:
Writable {
_writableState:
WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: true,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
bufferedRequest: null,
lastBufferedRequest: null,
pendingcb: 0,
prefinished: false,
errorEmitted: false,
emitClose: true,
autoDestroy: false,
bufferedRequestCount: 0,
corkedRequestsFree: [Object] },
writable: true,
domain: null,
_events:
[Object: null prototype] { response: [Function], error: [Function] },
_eventsCount: 2,
_maxListeners: undefined,
_options:
{ protocol: 'http:',
maxRedirects: 21,
maxBodyLength: 10485760,
path: '/api/pageForSlug?routeName=lud_form',
method: 'GET',
headers: [Object],
agent: undefined,
auth: undefined,
hostname: 'localhost',
port: '3000',
nativeProtocols: [Object],
pathname: '/api/pageForSlug',
search: '?routeName=lud_form' },
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function],
_currentRequest:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Socket],
connection: [Socket],
_header:
'GET /api/pageForSlug?routeName=lud_form HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nx-now-deployment-url: lps-5fxmi58fz.now.sh\r\nx-now-trace: staging-gru1\r\nx-real-ip: 177.45.65.235\r\nx-zeit-co-forwarded-for: 177.45.65.235\r\nupgrade-insecure-requests: 1\r\nx-forwarded-proto: https\r\nx-now-id: 7xllz-1567187175018-d0ce35475a9e\r\naccept-encoding: gzip, deflate\r\nuser-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36\r\nx-forwarded-for: 177.45.65.235\r\nx-forwarded-host: lps-danicuki.playax.now.sh\r\ndnt: 1\r\naccept-language: en-US,en;q=0.9\r\nconnection: close\r\nHost: localhost:3000\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/api/pageForSlug?routeName=lud_form',
_ended: false,
res: null,
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Circular],
[Symbol(isCorked)]: false,
[Symbol(outHeadersKey)]: [Object] },
_currentUrl: 'http://localhost:3000/api/pageForSlug?routeName=lud_form' },
response: undefined,
isAxiosError: true,
toJSON: [Function] }
END RequestId: 6e355938-32d5-459a-adf7-08fb97101e29
REPORT RequestId: 6e355938-32d5-459a-adf7-08fb97101e29 Duration: 281.53 ms Billed Duration: 300 ms Memory Size: 3008 MB Max Memory Used: 117 MB
RequestId: 6e355938-32d5-459a-adf7-08fb97101e29 Error: Runtime exited with error: exit status 1
Runtime.ExitError
How to make the server side API request work on all environments?
This can be achieved using a rewrite in your now.json Vercel file. No need for a hack.
{
"version": 2,
"builds": [
{
"src": "nuxt.config.js",
"use": "#nuxtjs/vercel-builder",
"config": {}
}
],
"rewrites": [
{
"source": "/api/:match*",
"destination": "https://myapidomain.com/:match*"
}
]
}
Are you ready for the ultimate hack?
This is actually a common issue. When calling zeit now, a server doesn't actually get propped up. As such, you'll need to do it yourself.
The only time I've encountered a need to do this, I used express to solve it. A quick and dirty solution is to create another script (proxy.js), and have something like so:
import express from 'express'
import proxy from 'express-http-proxy'
const app = express()
app.use('/', proxy(`https://myapidomain.com`))
export default app
This does nothing but set up a reverse proxy to your API.
From there, in your zeit config, find your builds key if you have one, and replace it with this:
"builds": [
{ "use": "#now/next", "src": "package.json" },
{ "use": "#now/node", "src": "proxy.js" }
]
This will prop up and execute proxy.js when you build using zeit now.
Do note that this does mean you'll need to get the host in proxy.js to match the "normal" host. If you know the format of your config file (which you should), it may be possible to import and parse that in order to have one source of truth.
Let me know if this worked.

Categories