I was following a REST API using Express with Firebase tutorial and came across this code.
//initialize express server
const app = express();
const main = express();
//add the path to receive request and set json as bodyParser to process the body
main.use('/api/v1', app);
Why do they create both app and main and why do they pass app to the '/api/v1' route?
It appears they are using app kind of like a sub-app whose routes are all prefixed with /api/v1. The usual and documented way of doing something like this would be to use an Express router instead of an app object and just have one app, but the app is a router also so it will apparently work.
So, the routes on their main app all work at the top level such as /about while the routes on their app app all work on /api/v1 such as /api/v1/users.
The more common way of doing this would be for the top level app to be named app and then use a router for the sub-app. You would replace:
//initialize express server
const app = express();
const main = express();
main.use('/api/v1', app);
with:
const app = express(); // top level app
const apiRouter = express.Router(); // api router
app.use('/api/v1', apiRouter); // hook in api router
Then top level routes go on the app object and api routes go on the apiRouter object.
But, this is just a more common convention. Either way will work.
Related
Tried to initialize the new twililo voice sdk and stuck on registering. Here is the debug image.
Without seeing the code, this looks like you are trying to use the SDK client-side. You want to use the sdk server-side. Here is how I import mine server-side.
const accountSid = Meteor.settings.private.twilioSid;
const authToken = Meteor.settings.private.twilioToken;
const smsClient = require("twilio")(accountSid, authToken);
You could then call a method from the client-side to interact with the Twilio API.
My firebase Nuxt app was recently converted to SSR from SPA mode. It was all working fine in SPA mode but when I tried to convert it, it generated a lot of errors. I tried to solve them one by one and I'm stuck with the error ReferenceError 'location' was not defined. I want to run my emulator because I want to test my other functions if it is running completely in SSR mode.
import firebaseTmp from "firebase/app";
import firebaseErrorsJa from "~/plugins/firebaseErrorsJa";
import "firebase/storage";
import "firebase/firestore";
import 'firebase/auth';
import 'firebase/functions';
const config = process.env.firebaseConfig;
if (!firebaseTmp.apps.length) {
firebaseTmp.initializeApp(config);
}
const db = firebaseTmp.firestore();
const functions = firebaseTmp.functions();
const firebase = firebaseTmp;
const firestore = firebaseTmp.firestore();
const storage = firebaseTmp.storage();
const auth = firebaseTmp.auth();
const firestoreTimestamp = firebaseTmp.firestore.Timestamp;
const serverTimestamp = firebaseTmp.firestore.FieldValue.serverTimestamp();
const firebaseErrors = firebaseErrorsJa;
if (location.hostname === "localhost") {
db.settings({
host: "localhost:8000",
ssl: false
});
functions.useEmulator("localhost", 5001);
auth.useEmulator('http://localhost:9099/');
}
export { db, firebase, firestore, auth, storage, firestoreTimestamp, serverTimestamp, functions, firebaseErrors }
I imported almost all libraries but still it does not work.
TAKE NOTE: This only happens when it is in SSR mode. Does this mean that location does not work in SSR mode?
I tried to take away the chunk of code that has 'location' in it. It works perfectly well locally but when I try to run my other functions, it generates CORS error. It accesses the link being used when we deploy our functions.
https://us-central1-talkfor-dev.cloudfunctions.net/v1-auth-updateUser
This is the link that was shown in the console
What I expected is that us-central1-talkfor-dev.cloudfunctions.net will be localhost:5000 since we are using the local development.
Do you have any Idea why is it like this?
Alex you understand that SSR mode is trying to render the website on the server. This can be your local running server instance, that means there is no window object for it to access location variable. Location is only accessible inside the browser. The SSR context is not in the browser, hence makes total sense.
Window.location here states.
Server vs Browser environments here states that
Because you are in a Node.js environment... You do not have access to the window or document objects... You can however use window or document by using the beforeMount or mounted hooks.
.
Why don't you try either of these,
use these beforeMount and mounted hooks like this.
beforeMount () {
window.alert('hello');
}
mounted () {
window.alert('hello');
}
using the process.client environment variable
if (process.client) {
// Do your stuff here
// I think this is what you are looking for.
}
I am trying to firebase to emulate locally for testing a react native app I am working on with expo. To that end I am trying to set the host of the functions and firestore to the proper port on local host.
After many iterations I finally found a weird combination of imports and calls that did not error. However, when I tried to run it with expo my App came up as a blank screen with no errors.
I am pretty lost at this point and the firebase documentation is confusing.
This is my current index.js:
import firebase from "firebase"
import 'firebase/firestore';
import 'firebase/functions';
import 'firebase/app';
const config = {
//config info
};
const fb = firebase.initializeApp(config);
const firestore = firebase.firestore();
const functions = firebase.functions();
if(__DEV__){
firestore.settings({
host : "localhost:9000",
ssl : false
});
functions.useFunctionsEmulator("http://localhost:5001");
}
const auth = fb.auth();
export { auth, functions, firestore }
The imports are very weird and I don't understand them but I got them from another stack overflow thread and it was the only thing that made it even get to the end of the file. Is there something very obvious I am missing about setting up the local emulator?
the issue you may be having with using the Local Emulator Suite and Expo together is that that "localhost" refers to the device you're using. If you're testing on a physical device, you need to point Firebase to the running instance on your computer. I wrote up a short explainer on it here:
https://dev.to/haydenbleasel/using-the-firebase-local-emulator-with-expo-s-managed-workflow-5g5k
I was able to get this to work on my machine via:
import * as firebase from 'firebase';
import '#firebase/firestore';
import '#firebase/functions';
const firebaseConfig = { /* config data */ }
firebase.initializeApp(firebaseConfig);
firebase.firestore().settings({ host: "localhost:8080", ssl: false });
firebase.functions().useFunctionsEmulator('http://localhost:5001');
In your attempt, I think your problem might be the the way you declared:
const fb = firebase.initializeApp(config);
and then forgot to use the initialized fb -- and instead you used firebase again.
try changing these lines:
const firestore = firebase.firestore();
const functions = firebase.functions();
to this:
const firestore = fb.firestore();
const functions = fb.functions();
or just use the firebase class directly instead of setting to a variable.
Edit:
I noticed that I actually had my firestore store host value set incorrectly so I edited it above - I had added http:// to the beginning but that's not what you want for that parameter.
Note - If you want to see exactly what your dev env is doing when trying to connect to your local firestore, add this line:
firebase.firestore.setLogLevel('debug');
I'm not exactly sure it's your only problem. But for firebase functions to work in an emulator using your android or iphone you need to change
functions.useFunctionsEmulator("http://localhost:5001") to
firebase.functions().useFunctionsEmulator("http://10.0.2.2:5001");
it's for an android emulator only. It's the IP adress of the device you need to reach.
This article does a good job of showing you how to get the right debugger host IP dynamically (if you're running Expo Go on a mobile phone).
basically, it all comes down to:
const origin = Constants.manifest.debuggerHost?.split(":").shift() || "localhost";
firebase.auth().useEmulator(`http://${origin}:9099/`);
firebase.firestore().useEmulator(origin, 8080);
firebase.functions().useEmulator(origin, 5001);
I am using Storybook to test my React UI components.
However, when I get to a point where my Action makes an Axios request, I get a 404 response.
Below is the code used in a react action file:
assume the axios instantiation, thunk implementation and action definitions.
getDelayedThunkRes: () => {
return (dispatch) => {
dispatch(delayedResActions.getInitialRes());
axios.get("/test").then(success => {
console.log(success);
}).then(err => {
console.log(err);
})
}
}
localhost:8080 is my real server that I want to connect to. Obviously it should throw me an error because my storybook is running on 9009. How can I connect the two?
Note, it works for my Create React App. Create React App package gives a provision to proxy all the calls to a server using "proxy" field in package.json
Are there any similar tools in Storybook, or is Storybook supposed to be used solely with static mock data?
Alright, I found an amazing post on how to create a middleware for React storybook for APIs
https://medium.com/#wathmal/adding-a-router-middle-ware-to-react-storybook-4d2585b09fc
Please visit the link. The guy deserves the due credit.
Here is my implementation of it in ES5 (somehow Storybook middleware is unable to transpile):
create this middleware.js inside .storybook directory:
const express = require('express');
const bodyParser = require('body-parser');
const expressMiddleWare = function(router) {
router.use(bodyParser.urlencoded({extended: false}));
router.use(bodyParser.json());
router.get('/test', function(req, res) {
res.send('Hello World!');
res.end();
});
}
module.exports = expressMiddleWare
Caveat: You will have to restart Storybook every time you make a change in the middleware.
With this, I am able to make a call from my react actions.
Next, I will try to implement express HTTP proxy middleware to redirect these storybook middleware calls to my real express server.
Edit 1:
The new technique seems to be using decorators, especially with stroybook-addon-headless.
Storybook add on for setting server urls
https://github.com/ArrayKnight/storybook-addon-headless
I am yet to try
Goal
Use the #slack/interactive-message package with firebase-functions to listen and respond to Slack messages and dialogs.
Question
I'm not sure how to use the #slack/interactive-message listener with firebase.
1) Do I use Firebase's functions.https.onRequest(), and somehow pass the req from Slack to slackInteractions.action()?
OR
2) Do I use app.use("/app", slackInteractions.expressMiddleware()); If so, where do slackInteractions.action()s go?
OR
3) Something else?
Code
// Express
import express = require("express");
const app = express();
const cors = require("cors")({
origin: "*"
});
app.use("*", cors);
// Firebase Functions SDK
import functions = require("firebase-functions");
const slackbotConfig = functions.config().slackbot;
const { createMessageAdapter } = require("#slack/interactive-messages");
const slackInteractions = createMessageAdapter(slackbotConfig.signing_secret);
app.use("/app", slackInteractions.expressMiddleware());
// Express route
app.post("/go", (req, res) => {
console.log("Hello from Express!");
res
.status(200)
.send("Hello from Express!")
.end();
});
exports.app = functions.https.onRequest(app);
exports.helloWorld = functions.https.onRequest((_req, res) => {
console.log("Hello from Firebase!");
res
.status(200)
.send("Hello from Firebase!")
.end();
});
tl;dr
I'm new to the details of Express and using middleware. Examples of the #slack/interactive-message show...
slackInteractions.start(port).then(() => {
console.log(`server listening on port ${port}`);
});
...and with Firebase Cloud Functions, this bit isn't relevant. I'm not sure how listeners, requests, and responses are integrated between Firebase and #slack/interactive-message
creator of #slack/interactive-messages here 👋
In short, your solution number 2 seems correct to me. While I don't have experience with Firebase functions, I have a pretty good understanding of express, and I'll provide some more details.
What is express middleware?
Express middleware is a name for a kind of function that processes an incoming HTTP request. All middleware functions can, on a request-by-request basis, choose to pre-process a request (usually by adding a property to the req argument), respond to the request, or post-process a request (like calculate the timing between the request and the response). It can do any one or combination of those things, depending on what its trying to accomplish. An express app manages a stack of middleware. You can think of this as a list of steps a request might work through before a response is ready. Each step in that list can decide to offer the response so that the next step isn't even reached for that request.
The cors value in your code example is a middleware function. It applies some rules about which origins your Firebase function should accept requests from. It applies those rules to incoming requests, and when the origin is not allowed, it will respond right away with an error. Otherwise, it allows the request to be handled by the next middleware in the stack.
There's another middleware in your example, and that's a router. A router is just a kind of middleware that knows how to split an app up into separate handlers based on the path (part of the URL) in the incoming request. Every express app comes with a built in router, and you attached a handler to it using the app.post("/go", () => {}); line of code in your example. Routers are typically the last middleware in the stack. They do have a special feature that people often don't realize. What are these handlers for routes? They are just more middleware functions. So overall, you can think of routers as a type of middleware that helps you divide application behavior based on the path of a request.
What does this mean for slackInteractions?
You can think of the slackInteractions object in your code as a router that always handles the request - it never passes the request onto the next middleware in the stack. The key difference is that instead of dividing application behavior by the path of the request, it divides the behavior using the various properties of a Slack interaction. You describe which properties exactly you care about by passing in constraints to the .action() method. The only significant difference between a typical router and slackInteractions, is that the value itself is not the express middleware, you produce an express middleware by calling the .expressMiddleware() method. It's split up like this so that it can also work outside of an express app (that's when you might use the .start() method).
Putting it together
Like I said, I don't have experience with Firebase functions specifically, but here is what I believe you should start with as a minimum for a function that only handles Slack interactions.
// Firebase Functions SDK
import functions = require("firebase-functions");
const slackbotConfig = functions.config().slackbot;
// Slack Interactive Messages Adapter
const { createMessageAdapter } = require("#slack/interactive-messages");
const slackInteractions = createMessageAdapter(slackbotConfig.signing_secret);
// Action handlers
slackInteractions.action('welcome_agree_button', (payload, respond) => {
// `payload` is an object that describes the interaction
console.log(`The user ${payload.user.name} in team ${payload.team.domain} pressed a button`);
// Your app does some asynchronous work using information in the payload
setTimeout(() => {
respond({ text: 'Thanks for accepting the code of conduct for our workspace' });
}, 0)
// Before the work completes, return a message object that is the same as the original but with
// the interactive elements removed.
const reply = payload.original_message;
delete reply.attachments[0].actions;
return reply;
});
// Express
import express = require("express");
const app = express();
app.use("/", slackInteractions.expressMiddleware());
exports.slackActions = functions.https.onRequest(app);