Koa: How to use koa.js + next in firebase functions

Created on 2 Jun 2019  路  15Comments  路  Source: koajs/koa

I want to use firebase functions to deploy the react application for shopify app

I am new to both next and koa

based on this repo the below code is how to host a simple react application in firebase

    const path = require('path')
    const functions = require('firebase-functions')
    const next = require('next')

    var dev = process.env.NODE_ENV !== 'production'
    var app = next({
      dev,
      conf: { distDir: `${path.relative(process.cwd(), __dirname)}/next` }
    })
    var handle = app.getRequestHandler()

    exports.next = functions.https.onRequest((req, res) => {
      console.log('File: ' + req.originalUrl) // log the page.js file that is being requested
      return app.prepare().then(() => handle(req, res))
    })

Which works correctly, no issue.

Then based on this tutorial from shopify I need to integrate koa and other dependencies in server.js, which in my case I believe it should be placed inside the firebase function. so I get to this code

```
const path = require('path')
const isomorphicFetch = require('isomorphic-fetch');
const Koa = require('koa');
const functions = require('firebase-functions')
const next = require('next');
const ShopifyConfig = require('./shopify.js');

const { default: createShopifyAuth } = require('@shopify/koa-shopify-auth');
const dotenv = require('dotenv');
const { verifyRequest } = require('@shopify/koa-shopify-auth');
const session = require('koa-session');

dotenv.config();

const port = parseInt(process.env.PORT, 10) || 3000;

var dev = process.env.NODE_ENV !== 'production'
var app = next({
  dev,
  conf: { distDir: `${path.relative(process.cwd(), __dirname)}/next` }
})
var handle = app.getRequestHandler()

const server = new Koa();

server.use(session(server));
server.keys = [ShopifyConfig.secretKey];

server.use(
  createShopifyAuth({
    apiKey: ShopifyConfig.key,
    secret: ShopifyConfig.secretKey,
    scopes: [],
    afterAuth(ctx) {
      const { shop, accessToken } = ctx.session;
      ctx.redirect('/');
    },
  }),
);

server.use(verifyRequest());

server.use(async (ctx) => {
  await handle(ctx.req, ctx.res);
  ctx.respond = false;
  ctx.res.statusCode = 200;

});

exports.next = functions.https.onRequest((req, res) => {
  console.log('File: ' + req.originalUrl) // 

  // This is old code
  // return app.prepare().then(() => {
  //   handle(req, res);
  // })

  // I tried this #1
  // server.callback(req, res);
})

// I tried this #2
// exports.next = functions.https.onRequest(server.callback);

// I tried this #3
// exports.next = functions.https.onRequest(server.callback());

// I tried this #4
exports.next = functions.https.onRequest((req, res) => {
  console.log('File: ' + req.originalUrl) 

  return app.prepare().then(() => {
    server.callback(req, res);
    //handle(req, res);
  })
})

```

My question is now based on koa what code should be in functions.https.onRequest ?

I tried #1, #2, #3, as well as this post

1 -> I get request timeout

2 -> I get request timeout

3 -> I get Cannot access middleware of undefined

4 -> I get request timeout

question

Most helpful comment

@kvindasAB Thanks
Your solution works

All 15 comments

@RezaRahmati server.callback is not the callback by itself, rather a function that generates the callback from your configuration I assumed.

You need to do this:

server.callback()(req, res);

That should do the trick I think.

@kvindasAB thanks I will try, btw as I remember callback() was returning void not a function, however I try.

Application#callback returns a requestListener function compatible with node http.

@RezaRahmati Have you resolved this issue?

@gdayton I didn't tried it yet ;) I will try at weekend

@RezaRahmati Ended up hosting the app on GCP AppEngine and will use Functions for other purposes

I'm going to close this as it seems to be answered and/or not an issue anymore. Please feel free to re-open if you're still having problems with this.

@kvindasAB Thanks
Your solution works

const functions = require('firebase-functions');
const app = new Koa();

...

exports['http'] = functions.https.onRequest(app.callback());

@kvindasAB Thanks
Your solution works

Were you able to get the authentication part working? It seems to keep going in a loop and never authenticate.

createShopifyAuth({
       apiKey: ShopifyConfig.key,
       secret: ShopifyConfig.secretKey,
       scopes: [],
       afterAuth(ctx) {
         const { shop, accessToken } = ctx.session;
         ctx.redirect('/');
       },
     }),

This does not seem to be creating the /auth and /auth/callback routes properly in the firebase cloud functions environment. Any ideas?

@kvindasAB Thanks
Your solution works

Were you able to get the authentication part working? It seems to keep going in a loop and never authenticate.

createShopifyAuth({
       apiKey: ShopifyConfig.key,
       secret: ShopifyConfig.secretKey,
       scopes: [],
       afterAuth(ctx) {
         const { shop, accessToken } = ctx.session;
         ctx.redirect('/');
       },
     }),

This does not seem to be creating the /auth and /auth/callback routes properly in the firebase cloud functions environment. Any ideas?

ever find a solution?

i have gotten a little farther into this and will have a fix soon i hope haha

Wow, if you have the auth loop... Please save yourself the time from feeling like a scrub and forgetting firebase session management is different and just install this:

https://community.shopify.com/c/Shopify-Apps/Just-Released-Koa-Firebase-Session-Store/td-p/609260

thank me later

Was anyone here able to make Shopify OAuth process work with Firebase functions?
I'd love to see a snippet of that code that make the magic happens, I tried many variations and constantly failed... I am getting an infinite redirect loop now.
Help would be greatly appreciated.
Thanks!

Personally, I ended up just making it from scratch after wasting about 2 weeks trying to get the Shopify sample w/ shopify libs to work in firebase...

Took about a half day to get it working. Shopify has great sample code, so it wasn't too bad at all,
https://shopify.dev/tutorials/authenticate-with-oauth

NOTE: you can use crypto.createHmac and cryto.timingSafeEqual in node for HMAC verification.

Also, (I think) firebase only supports session cookies with an underscore, like "_session". So, that's one of the reasons it doesn't work (amongst others)... Fundamentally I think there are so many issues with the Shopify sample app because firebase functions aren't really raw node servers (which is what the Shopify libs are made for). Firebase functions are node apps that (sort of) are already inside an express middleware that runs after a bunch of other middlewares like body parser, form data parser, etc.

Hope this helps!

Actually, I did get it to work long back! There are multiple issues at different levels, after wasting (or using) a lot of time I was able to get it to work. Your missing piece of puzzle is that Next.js will not work with Koa server with Firebase in 'dev' mode! It will work fine in production mode. To test it out, build your Nextjs app with npm run build (which should execute next build) and then try with Firebase functions.

In your server.js file, you can export the server like

// functions/server.js
.
..
...
module.exports = server;

The firebase function should look like this:

// functions/index.js
const server = require("./server");
exports.cloudNext = functions.https.onRequest(async (req, res) => {
  return server.callback()(req, res);
});

For development you can create a separate local server like below to serve your nextjs app in dev mode

// dev-server.js
const server = require("./functions/server");
const port = parseInt(process.env.PORT, 10) || 5000;
server.listen(port, () => {
    console.log(`> Ready on http://localhost:${port}`);
});

Let me know if face any issue.

Was anyone here able to make Shopify OAuth process work with Firebase functions?
I'd love to see a snippet of that code that make the magic happens, I tried many variations and constantly failed... I am getting an infinite redirect loop now.
Help would be greatly appreciated.
Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tracker1 picture tracker1  路  3Comments

coodoo picture coodoo  路  4Comments

rally25rs picture rally25rs  路  4Comments

ilkkao picture ilkkao  路  4Comments

usernameisalreadytaken2014 picture usernameisalreadytaken2014  路  4Comments