Referring to own packages in nixosModules import

So I am making a flake for a little project. I am struggling with splitting it up into multiple files. I am both outputting packages and a nixos module. I would prefer to break up the nixos module in related parts. But I want to include a package I am making in both that nixos module and the packages part of the flake. One issue that I have is that most examples I have seen have the nixosModules output free from any references to system, but the package itself has a dependency on system somewhere.

Here are simplified versions of the structure I have been aiming at:

flake.nix

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
    pyproject-nix = {
      url = "github:pyproject-nix/pyproject.nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    uv2nix = {
      url = "github:pyproject-nix/uv2nix";
      inputs.pyproject-nix.follows = "pyproject-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    pyproject-build-systems = {
      url = "github:pyproject-nix/build-system-pkgs";
      inputs.pyproject-nix.follows = "pyproject-nix";
      inputs.uv2nix.follows = "uv2nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs =
    {
      self,
      nixpkgs,
      flake-utils,
      uv2nix,
      pyproject-nix,
      pyproject-build-systems,
      ...
    }@inputs:
    flake-utils.lib.eachDefaultSystem (system: let
      inherit (nixpkgs) lib;

      workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };

      # Create package overlay from workspace.
      overlay = workspace.mkPyprojectOverlay {
        sourcePreference = "wheel"; # or sourcePreference = "sdist";
      };

      pyprojectOverrides = final: prev: {
        # various overrides
      };

      pkgs = import nixpkgs {
        inherit system;
        config.allowUnfree = true;
        # TODO: maybe use overlay here?
      };

      python = pkgs.python312;
      pythonSet = (pkgs.callPackage pyproject-nix.build.packages { inherit python; })
        .overrideScope (lib.composeManyExtensions [
          pyproject-build-systems.overlays.default
          overlay
          pyprojectOverrides
        ]);

      venv = (pythonSet.mkVirtualEnv "env" workspace.deps.default).overrideAttrs(old: {
        venvIgnoreCollisions = [ "*" ];
      });

      # Wrapper script to run FastAPI inside the environment
      fastapiRunner = pkgs.writeShellScriptBin "fastapi-server" ''
        cd ${venv}/lib/python3.12/site-packages
        ls
        exec ${venv}/bin/python -m fastapi --verbose run --app hello_world.main:app
      '';
    in
    {
      packages = {
        default = venv;
        web-service-env = venv;
      };

      # Make hello runnable with `nix run`
      apps = {
        default = {
          type = "app";
          program = "${self.packages.${system}.default}/bin/geotopo";
        };
      };

    }) // {
      nixosModules.default = {
        imports = [
          ./nix
        ];
      };
    };
}

/nix/default.nix

{ config, lib, pkgs, ...}:
with lib; let
  cfg = config.services.something;
in {
  imports = [
    ./web-service
  ];

  options.services = {
    # various options
  };

  config = mkIf cfg.enable {
    # set a few things
  };
}

/nix/web-service/default.nix

{ config, lib, pkgs, utils, ...}:
with lib; let
  cfg = config.services.thingy.web-service;
  # TODO: how to refer to own package?
  web-service-env = pkgs.web-service-env;
in {
  imports = [
  ];

  options.services.thingy.web-service = {
    enable = mkOption {
      type = types.bool;
      default = false;
      example = true;
      description = ''
        Whether or not to enable the geotopo web service module.
      '';
    };

    package = mkOption {
      type = types.package;
      default = web-service-env;
      description = "package to use for this service (defaults to the one in the flake)";
    };
  };

  config = mkIf cfg.enable {
    assertions = [
    ];

    # Systemd service
    systemd.services.my-web-service = {
      after = [ "network.target" ];
      wantedBy = [ "multi-user.target" ];
      serviceConfig = {
        ExecStart = "${cfg.package}/bin/app";
      };
    };
  };
}

In this last file I am trying to access the package somehow. I have tried quite a few approaches but to no avail.

You don’t need this, just set meta.mainProgram = "geotopo"; in your package.
Also, using venvs is definitely a strange way to package, just use buildPythonApplication or such: https://nixos.org/manual/nixpkgs/unstable/#building-packages-and-applications

Pass inputs into the module system and use inputs.self.packages.${pkgs.stdenv.hostPlatform.default}.default

I cut and changed a few things like you suggested.

Regarding passing the inputs I structured things like the Hyprland flake.

flake.nix - Hyprland equivalent

      nixosModules.default = {
        imports = [
          (import ./nix inputs)
        ];
      };

Imported module file - Hyprland equivalent

inputs: { config, lib, pkgs, ...}: {
  # configuration
}

That will make it harder to find error messages. At least set _file?

You can skip the intermediate module:

nixosModules.default = import ./nix inputs;