Good evening everyone. Today I tried writing a code to add a user to mongodb database which requires the following fields - name, email, accessType, password and image - but unfortunately I have been having issues uploading images with the Next js api route. I have googled for hours and no result. I disabled the bodyParser to allow files to be read but that only made things worst. It could no longer read any data at all from the req.body. I am currently using postman for testing the API. I will be very grateful if someone could please help me out before I lose my mind.
import multer from "multer";
import dbConnect from "/utils/dbConnect";
import path from "path";
import Manager from "/models/Manager";
dbConnect();
export const config = {
api: {
bodyParser: false
}
}
const manager = async (req, res) => {
const {method} = req;
const { name, email, accessType, password } = req.body;
// const { filename } = req.file;
// console.log(filename);
console.log(name, email, accessType, password, "values");
switch(method) {
case "POST":
try {
}
catch(error) {
}
res.status(200).json({success: true, name});
}
}
export default manager;
Related
In my Expo (react-native) application, I want to do the upload task even if the application is in the background or killed.
the upload should be done to firebase storage, so we don't have a REST API.
checked out the Expo task manager library, but I could not figure out how it should be done. is it even possible to achieve this goal with Expo? is the TaskManager the correct package for this task?
there are only some Expo packages that could be registered as a task (e.g. backgroundFetch), and it is not possible to register a custom function (in this case uploadFile method).
I even got more confused as we should enable add UIBackgroundModes key for iOS but it only has audio,location,voip,external-accessory,bluetooth-central,bluetooth-peripheral,fetch,remote-notification,processing as possible values.
I would appreciate it if you can at least guide me on where to start or what to search for, to be able to upload the file even if the app is in the background is killed/terminated.
import { getStorage, ref, uploadBytes } from "firebase/storage";
const storage = getStorage();
const storageRef = ref(storage, 'videos');
const uploadFile = async (file)=>{
// the file is Blob object
await uploadBytes(storageRef, file);
}
I have already reviewed react-native-background-fetch, react-native-background-upload, react-native-background-job . upload should eject Expo, job does not support iOS, and fetch is a fetching task designed for doing task in intervals.
if there is a way to use mentioned libraries for my purpose, please guide me :)
to my understanding, the Firebase Cloud JSON API does not accept files, does it ? if so please give me an example. If I can make storage json API work with file upload, then I can use Expo asyncUpload probably without ejecting.
I have done something similar like you want, you can use expo-task-manager and expo-background-fetch. Here is the code as I used it. I Hope this would be useful for you.
import * as BackgroundFetch from 'expo-background-fetch';
import * as TaskManager from 'expo-task-manager';
const BACKGROUND_FETCH_TASK = 'background-fetch';
const [isRegistered, setIsRegistered] = useState(false);
const [status, setStatus] = useState(null);
//Valor para que se ejecute en IOS
BackgroundFetch.setMinimumIntervalAsync(60 * 15);
// Define the task to execute
TaskManager.defineTask(BACKGROUND_FETCH_TASK, async () => {
const now = Date.now();
console.log(`Got background fetch call at date: ${new Date(now).toISOString()}`);
// Your function or instructions you want
return BackgroundFetch.Result.NewData;
});
// Register the task in BACKGROUND_FETCH_TASK
async function registerBackgroundFetchAsync() {
return BackgroundFetch.registerTaskAsync(BACKGROUND_FETCH_TASK, {
minimumInterval: 60 * 15, // 1 minutes
stopOnTerminate: false, // android only,
startOnBoot: true, // android only
});
}
// Task Status
const checkStatusAsync = async () => {
const status = await BackgroundFetch.getStatusAsync();
const isRegistered = await TaskManager.isTaskRegisteredAsync(
BACKGROUND_FETCH_TASK
);
setStatus(status);
setIsRegistered(isRegistered);
};
// Check if the task is already register
const toggleFetchTask = async () => {
if (isRegistered) {
console.log('Task ready');
} else {
await registerBackgroundFetchAsync();
console.log('Task registered');
}
checkStatusAsync();
};
useEffect(() => {
toggleFetchTask();
}, []);
Hope this isn't too late to be helpful.
I've been dealing with a variety of expo <-> firebase storage integrations recently, and here's some info that might be helpful.
First, I'd recommend not using the uploadBytes / uploadBytesResumable methods from Firebase. This Thread has a long ongoing discussion about it, but basically it's broken in v9. Maybe in the future the Firebase team will solve the issues, but it's pretty broken with Expo right now.
Instead, I'd recommend either going down the route of writing a small Firebase function that either gives a signed-upload-url or handles the upload itself.
Basically, if you can get storage uploads to work via an http endpoint, you can get any kind of upload mechanism working. (e.g. the FileSystem.uploadAsync() method you're probably looking for here, like #brentvatne pointed out, or fetch, or axios. I'll show a basic wiring at the end).
Server Side
Option 1: Signed URL Upload.
Basically, have a small firebase function that returns a signed url. Your app calls a cloud function like /get-signed-upload-url , which returns the url, which you then use. Check out: https://cloud.google.com/storage/docs/access-control/signed-urls for how you'd go about this.
This might work well for your use case. It can be configured just like any httpsCallable function, so it's not much work to set up, compared to option 2.
However, this doesn't work for the firebase storage / functions emulator! For this reason, I don't use this method, because I like to intensively use the emulators, and they only offer a subset of all the functionalities.
Option 2: Upload the file entirely through a function
This is a little hairier, but gives you a lot more fidelity over your uploads, and will work on an emulator! I like this too because it allows doing upload process within the endpoint execution, instead of as a side effect.
For example, you can have a photo-upload endpoint generate thumbnails, and if the endpoint 201's, then you're good! Rather than the traditional Firebase approach of having a listener to cloud storage which would generate thumbnails as a side effect, which then has all kinds of bad race conditions (checking for processing completion via exponentiational backoff? Gross!)
Here are three resources I'd recommend to go about this approach:
https://cloud.google.com/functions/docs/writing/http#multipart_data
https://github.com/firebase/firebase-js-sdk/issues/5848
https://github.com/mscdex/busboy
Basically, if you can make a Firebase cloud endpoint that accepts a File within formdata, you can have busboy parse it, and then you can do anything you want with it... like upload it to Cloud Storage!
an outline of this:
import * as functions from "firebase-functions";
import * as busboy from "busboy";
import * as os from "os";
import * as path from "path";
import * as fs from "fs";
type FieldMap = {
[fieldKey: string]: string;
};
type Upload = {
filepath: string;
mimeType: string;
};
type UploadMap = {
[fileName: string]: Upload;
};
const MAX_FILE_SIZE = 2 * 1024 * 1024; // 2MB
export const uploadPhoto = functions.https.onRequest(async (req, res) => {
verifyRequest(req); // Verify parameters, auth, etc. Better yet, use a middleware system for this like express.
// This object will accumulate all the fields, keyed by their name
const fields: FieldMap = {};
// This object will accumulate all the uploaded files, keyed by their name.
const uploads: UploadMap = {};
// This will accumulator errors during the busboy process, allowing us to end early.
const errors: string[] = [];
const tmpdir = os.tmpdir();
const fileWrites: Promise<unknown>[] = [];
function cleanup() {
Object.entries(uploads).forEach(([filename, { filepath }]) => {
console.log(`unlinking: ${filename} from ${path}`);
fs.unlinkSync(filepath);
});
}
const bb = busboy({
headers: req.headers,
limits: {
files: 1,
fields: 1,
fileSize: MAX_FILE_SIZE,
},
});
bb.on("file", (name, file, info) => {
verifyFile(name, file, info); // Verify your mimeType / filename, etc.
file.on("limit", () => {
console.log("too big of file!");
});
const { filename, mimeType } = info;
// Note: os.tmpdir() points to an in-memory file system on GCF
// Thus, any files in it must fit in the instance's memory.
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
uploads[filename] = {
filepath,
mimeType,
};
const writeStream = fs.createWriteStream(filepath);
file.pipe(writeStream);
// File was processed by Busboy; wait for it to be written.
// Note: GCF may not persist saved files across invocations.
// Persistent files must be kept in other locations
// (such as Cloud Storage buckets).
const promise = new Promise((resolve, reject) => {
file.on("end", () => {
writeStream.end();
});
writeStream.on("finish", resolve);
writeStream.on("error", reject);
});
fileWrites.push(promise);
});
bb.on("close", async () => {
await Promise.all(fileWrites);
// Fail if errors:
if (errors.length > 0) {
functions.logger.error("Upload failed", errors);
res.status(400).send(errors.join());
} else {
try {
const upload = Object.values(uploads)[0];
if (!upload) {
functions.logger.debug("No upload found");
res.status(400).send("No file uploaded");
return;
}
const { uploadId } = await processUpload(upload, userId);
cleanup();
res.status(201).send({
uploadId,
});
} catch (error) {
cleanup();
functions.logger.error("Error processing file", error);
res.status(500).send("Error processing file");
}
}
});
bb.end(req.rawBody);
});
Then, that processUpload function can do anything you want with the file, like upload it to cloud storage:
async function processUpload({ filepath, mimeType }: Upload, userId: string) {
const fileId = uuidv4();
const bucket = admin.storage().bucket();
await bucket.upload(filepath, {
destination: `users/${userId}/${fileId}`,
{
contentType: mimeType,
},
});
return { fileId };
}
Mobile Side
Then, on the mobile side, you can interact with it like this:
async function uploadFile(uri: string) {
function getFunctionsUrl(): string {
if (USE_EMULATOR) {
const origin =
Constants?.manifest?.debuggerHost?.split(":").shift() || "localhost";
const functionsPort = 5001;
const functionsHost = `http://${origin}:${functionsPort}/{PROJECT_NAME}/${PROJECT_LOCATION}`;
return functionsHost;
} else {
return `https://{PROJECT_LOCATION}-{PROJECT_NAME}.cloudfunctions.net`;
}
}
// The url of your endpoint. Make this as smart as you want.
const url = `${getFunctionsUrl()}/uploadPhoto`;
await FileSystem.uploadAsync(uploadUrl, uri, {
httpMethod: "POST",
uploadType: FileSystem.FileSystemUploadType.MULTIPART,
fieldName: "file", // Important! make sure this matches however you want bussboy to validate the "name" field on file.
mimeType,
headers: {
"content-type": "multipart/form-data",
Authorization: `${idToken}`,
},
});
});
TLDR
Wrap Cloud Storage in your own endpoint, treat it like a normal http upload, everything plays nice.
I am building out a webpage which needs to make a call to the Google Geocoder api.
In order to hide the api key from public view, I am trying to set up server middleware to act as a REST api endpoint.
I have checked through all of the documentation and copied all of it, but the response is always the same. I receive the entirety of the html body back from the axios request rather than anything else I send back via express.
In my component I have the following code:
computed: {
normalizedAddress() {
return `${this.member.address.street} ${this.member.address.city}, ${this.member.address.state} ${this.member.address.zip}`.replace(
/\s/g,
'+'
)
}
},
methods: {
async getLocation() {
try {
const res = await axios.get(
`/api/geocode/${this.normalizedAddress}`
)
console.log(res)
} catch (err) {
console.log(err)
}
}
},
In nuxt.config.js I have this setup
serverMiddleware: ['~/api/geocode.js'],
In the root of my project I have an api folder with geocode.js stored there.
geocode.js is below
import express from 'express';
import axios from "axios";
let GEO_API = "MY_API_KEY"
const app = express();
app.use(express.json());
app.get("/", async (req, res) => {
const uri = `https://maps.googleapis.com/maps/api/geocode/json?address=${req.params.address}&key=${GEO_API}`
try {
const code = await axios.get(uri);
if (code.status !== "OK") {
return res.status(500).send(code.status)
}
return res.status(200).send(code);
} catch (err) {
return res.status(500).send(err);
}
});
export default {
path: "/api/geocode/:address",
handler: app
}
Again. The response always has the entire html document from the website sent back (some 100 pages of code).
Even when I set the response to fixed text, that is not sent.
The only detail I can think of that might be interrupting it is that I have my own custom routing setup using the #nuxtjs/router build module in use.
How can I upload images to Strapi server?
I use the GraphQL plugin.
I prefer a single endpoint for all of API
I found an article about how to manage file uploads but I have some questions
Do I need to convert my image to a Base64 string?
My production server will be PostgreSQL. I was planning to store images as Blob. But now it turns out I can only send Form-Data to Strapi servers.
Do I need something like apollo-upload-client?
Note: Currently I use vue-apollo and nativescript-vue as frontend.
import VueApollo from "vue-apollo";
import { ApolloClient, InMemoryCache, HttpLink } from "apollo-boost";
import { setContext } from "apollo-link-context";
If you are trying to upload files with GraphQL, I suggest you check this gist - https://gist.github.com/alexandrebodin/fedc71c8513bfbb6283cc90ae62755c5
You should have all the information you need to achieve that.
Thanks to the answer of #Jim LAURIE I made my node work:
import { GraphQLClient, gql } from "graphql-request"
import { createReadStream } from "fs";
[...]
const endpoint = 'http://localhost:1337/graphql';
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
authorization: `Bearer ${jwt}`,
},
});
try {
const data = await graphQLClient.request( gql`
mutation($file: Upload!) {
upload(file: $file) {
id
name
}
}
`, {
file: createReadStream(__dirname + '/Strapi/test/picture.jpg') // ⚠ Ensure path is correct or the stream will never send data and you will have Socket Hang out error
});
console.info("ID of file:" + data.upload.id);
console.info(data)
console.info("data")
} catch (error) {
console.error(error)
console.error("error")
}
If you don't know how get the JWT check the units testing docs of Strapi, translate to GraphQL should be easy.
I am attempting to use the Apollo GraphQL Client for React Native. However, in some parts of my app I need to do a mutation on the GraphQL data, in such a way that the interface should not be exposed to the user.
For instance, on my sign up page, I want to create a user in the database, but only after I have gone through and verified everything, created a uuid, etc. (things that require a class). If the call is sucessful, I want to imediately move on to the home page of the app. If not, I want to notify the user.
As such, I need access to do a GraphQL request, without hooks and just using callbacks to change the UI. Is this possible, and how could this be done?
The documentation does a bad job of explaining it, but you can simply call query or mutate on the ApolloClient object. https://www.apollographql.com/docs/react/api/core/ApolloClient/#apolloclient-functions
Compared to the other answer, this is probably better than making a raw call with just fetch because it uses the same cache layer as the rest of your application, instead of going around it.
const apolloClient = new ApolloClient({
uri: "/graphql",
cache: new InMemoryCache()
})
const qr = gql`
query {
getCustomers() {
name
}
}
`
const result = await apolloClient.query({
query: qr ,
variables: {}
})
Yes, its possible.
A call to the GraphQL service simply expects a key-value pair of query or mutation in the body with the query/mutation you're trying to send.
You can do this with a simple fetch request as POST, or a cURL, or via postman... It doesn't really matter as long as its a POST request.
See also here.
Yes, It is possible as a matter of fact I am leaving sample classes that can be used for both query and mutation.
First, configure your application to work with graphQl.
Wrap your app with the provider.
import { client } from './config/connection';
import { ApolloProvider } from '#apollo/client';
<ApolloProvider client={client}>
<App/>
</ApolloProvider>
Here is the client that we want to
import { ApolloClient, ApolloLink, InMemoryCache } from '#apollo/client';
export const client = new ApolloClient({
cache: new InMemoryCache(),
uri: 'http://localhost:4000/graphql',
});
Operations.js (Contains Queries And Mutations gql)
import { gql } from '#apollo/client';
export const Query_SignIn = gql`
query Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
name
}
}
`;
export const Mutate_SignUp = gql`
mutation SignUp($name: String!, $email: String!, $password: String!, $passwordConfirmation: String!) {
signUp(name: $name, email: $email, password: $password, passwordConfirmation: $passwordConfirmation) {
name
}
}
`;
A Class using query instead of useQuery hook
import { Query_SignIn } from '../../../operations';
class login {
constructor(client) {
this._client = client;
}
async signIn(email, password) {
const response = await this._client.query({
query: Query_SignIn,
variables: {
email,
password,
},
});
return response;
}
}
export default login;
A class using mutate instead of useMutation
import { Mutate_SignUp } from '../../../operations';
class register {
constructor(client) {
this._client = client;
}
async signUp(accountType, name, email, password, passwordConfirmation) {
const response = await this._client.mutate({
mutation: Mutate_SignUp,
variables: {
name,
email,
password,
passwordConfirmation,
},
});
return response;
}
}
export default register;
How can I have a Foxx service use base collections for auth operations? For example I want the User management tutorial at https://docs.arangodb.com/3.3/Manual/Foxx/Users.html
to use collections "users" and "sessions" instead of "test_users" and "test_sessions", where "test" is the name of my mountpoint.
I want to run multiple services all working off the same base collections. But if I go with whats given in the tutorials, I end up with auth collections and routes which are specific to a service, which doesnt males much sense to me.
My setup.js is;
'use strict';
const db = require('#arangodb').db;
const sessions = module.context.collectionName('sessions');
const users = module.context.collectionName('users');
if (!db._collection(sessions)) {
db._createDocumentCollection(sessions);
}
if (!db._collection(users)) {
db._createDocumentCollection(users);
}
db._collection(users).ensureIndex({
type: 'hash',
fields: ['username'],
unique: true
});
and my index.js is;
'use strict';
const joi = require('joi');
const createAuth = require('#arangodb/foxx/auth');
const createRouter = require('#arangodb/foxx/router');
const sessionsMiddleware = require('#arangodb/foxx/sessions');
// const db = require('#arangodb').db;
const auth = createAuth();
const router = createRouter();
const users = db._collection('users');
const sessions = sessionsMiddleware({
storage: module.context.collection('sessions'),
transport: 'cookie'
});
module.context.use(sessions);
module.context.use(router);
// continued
router.post('/signup', function (req, res) {
const user = {};
try {
user.authData = auth.create(req.body.password);
user.username = req.body.username;
user.perms = [];
const meta = users.save(user);
Object.assign(user, meta);
} catch (e) {
// Failed to save the user
// We'll assume the uniqueness constraint has been violated
res.throw('bad request', 'Username already taken', e);
}
req.session.uid = user._key;
req.sessionStorage.save(req.session);
res.send({success: true});
})
.body(joi.object({
username: joi.string().required(),
password: joi.string().required()
}).required(), 'Credentials')
.description('Creates a new user and logs them in.');
I tried using const users = db._collection('users'); instead of const users = module.context.collection('users'); but that throws swagger api errors.
to achieve that you need to change the assignment of collection names from module.context.collectionName('nameOfCollection') to 'nameOfCollection' in all files, because module.context.collectionName prefixes string with name of service
so
setup.js
const sessions = 'sessions';
const users = 'users';
index.js
const users = db._collection('users');
const sessions = sessionsMiddleware({
storage: 'sessions',
transport: 'cookie'
});
however, that approach is antipattern for case when more services need access to same underlying collections (for example teardown of one service can delete those collections for other services).
for that case you should utilize dependencies, only your auth service should have access to its own collections and other services should have auth service as dependency and access auth data through auth service.
auth service needs to have
in manifest.json
"provides": {
"myauth": "1.0.0"
}
in index.js or what file you pointing as main in manifest.json
module.exports = {
isAuthorized (id) {
return false; // your code for validating if user is authorized
}
};
consuming service needs to have
in manifest.json
"dependencies": {
"myauth": {
"name": "myauth",
"version": "^1.0.0",
"description": "Auth service.",
"required": true
}
}
and then you can register it and call it
const myauth = module.context.dependencies.myauth;
if (myauth.isAuthorized()) {
// your code
} else {
res.throw(401);
}
for further steps in terms of authorization of requests check how to use Middleware and Session middleware
god speed