'How to avoid circular dependency in lua without global variables?

I'm using OpenResty and my app is setup as:

app/
   clients/
      photoClient.lua
   init.lua
   auth.lua

Let's say photoClient has both an unauthenticated and a authenticated endpoint (similar to an avatar endpoint that is accessible without logging in, but there may be private photos that you need to login first)

So in terms of dependencies, I have:

-- auth.lua

local photoClient = require('app.clients.photoClient')
-- this is used to show avatar on the login page

local auth = {}

auth.isAuthenticated = function ()
   -- logic to check authentication
end

return auth

and the client is

-- photoClient.lua
local auth = require('app.auth')

local photoClient = {}
photoClient.privateEndpoint = function()
   if (!auth.isAuthenticated()) {
       ngx.exit(403)
   }
   ...
end

photoClient.getAvator = function() {
   -- this is a public method used by auth
}

return photoClient

This is giving me a circular dependency issue. I've seen on other SO post that you can use global variables, i.e. to do photoClient = photoClient or require('app.clients.photoClient') but I do not want to use global variables and want to keep each module scoped to itself.

How can I do this?



Solution 1:[1]

Found the solution on LuaFlare Documentation by Kate Adams: basically add package.loaded[...] = your_module before you do return your_module to every file, i.e.

-- auth.lua
local auth = {}
package.loaded[...] = auth

local photoClient = require('app.clients.photoClient')

...

return auth


-- photoClient.lua
local photoClient = {}
package.loaded[...] = photoClient

local auth = require('app.auth')

...

return photoClient

Fixed the problem. Here is the link to the book's page for anyone who is interested to read more.

Solution 2:[2]

Two main solutions:

Split

Split up auth into two modules that handle different aspects: an auth module for common logic that might be used by photoClient and a login module that is the login page.

This solution will prevent the same problem in other cases where you need to use auth.

Inject

In parent code (init.lua) that owns both of these modules, inject one into the other (call a function that passes the module).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Egor Skriptunoff
Solution 2 idbrii