Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Where does the generated PrismaClient belong? #60

Open
disbelief opened this issue Jan 28, 2024 · 4 comments
Open

Where does the generated PrismaClient belong? #60

disbelief opened this issue Jan 28, 2024 · 4 comments

Comments

@disbelief
Copy link

disbelief commented Jan 28, 2024

Hi there, thanks for this fantastic example of using SST with Prisma on a Layer.

I've copied your stack code into my SST project, and I'm able to get the PrismaLayer built with the @prisma/* packages installed, but I'm running into problems executing my lambda functions that use the PrismaClient.

It seems that the .prisma directory is expected to be somewhere but it's not. I can't figure out where it should be. I'd assume it belongs on the Layer itself, but the bundle command actually executes rm -rf .prisma

The error I'm seeing in my lambda function is:

Error: @prisma/client did not initialize yet. Please run \"prisma generate\" and try to import it again.
        In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues
            at new PrismaClient (/opt/nodejs/node_modules/.prisma/client/index.js:43:11)
            at getClient (file:///var/task/packages/functions/src/test.mjs:16476:12)
            at async file:///var/task/packages/functions/src/test.mjs:16491:19
            at async file:///var/task/packages/functions/src/test.mjs:16335:16

I have "postinstall": "npm run generate" in my package.json but it doesn't seem to be generating the client (I don't see anything in the function code).

Should the generated Prisma client be on the layer or in the lambda package itself?

Any help/advice would be appreciated.

@revmischa
Copy link
Member

In this project the generated prisma client is bundled with each function. Whether it makes sense to put it in the layer or in bundled function code is a good question for debate, but it's certainly vastly easier for development and the layer build to have the generated client be in the function code because you don't have to rebuild the layer every time you make a code change.

I think esbuild takes care of the bundling automatically. You need to make sure generate is run https://github.com/jetbridge/sst-prisma/blob/master/package.json#L14 to generate the client. It should be run automatically via the postinstall hook.

@disbelief
Copy link
Author

I see your point re not rebuilding the layer every time, that makes sense.

But I'm not clear on how the generated prisma client makes it into the function bundle without manually doing it?

If I use copyFiles to copy the local .prisma folder into the function bundle, it works:

# stacks/layers.ts

app.addDefaultFunctionLayers([prismaLayer]);
  app.addDefaultFunctionEnv(prismaLayer.environment);
  app.setDefaultFunctionProps({
    copyFiles: [
      { from: 'packages/core/prisma/schema.prisma', to: 'src/schema.prisma' },
      { from: 'node_modules/.prisma/', to: '.prisma/' },
    ],
    runtime: 'nodejs20.x',
    timeout: '90 seconds',
    nodejs: {
      format: 'esm',
      esbuild: {
        banner: { js: ESM_REQUIRE_SHIM },
        external: LAYER_MODULES.concat(prismaLayer.externalModules),
        sourcemap: true,
      },
    },
  });

But the problem now is this dir has both libquery_engine files for my local env (darwin-arm64) and rhel-openssl-3.0 so the size is quite large (>30 MB). Even with just the rhel engine that .prisma dir it's still very large (>15MB).

@disbelief
Copy link
Author

Found a way around uploading the libquery_engine files with the functions...

Maybe something has changed in a more recent version of Prisma? Because it appears that @prisma/engines does not actually contain all of the Prisma engines as their documentation states, just a default (incorrect) one.

The /opt/nodejs/node_modules/@prisma/engines dir on the PrismaLayer only contains a single libquery_engine-linux-arm64-openssl-3.0.x.so.node

However I resolved this by setting PRISMA_CLI_BINARY_TARGETS=rhel-openssl-3.0.x in the layer's create bundle command before executing npm install:

// stacks/resources/prismaLayer.ts

const createBundleCommand = [
  // create asset bundle in docker
  'bash',
  '-c',
  [
    `echo "Installing ${modulesToInstallArgs}"`,
    'export PRISMA_CLI_BINARY_TARGETS=rhel-openssl-3.0.x',
    'mkdir -p /tmp/npm && pushd /tmp/npm && HOME=/tmp npm i --no-save --no-package-lock npm@latest && popd',

    // ... rest of command
  ];

// ... bundleCommandHash and Code.fromAsset ...

// hint for prisma to find the engine
this.environment = app.local
  ? {}
  : {
      PRISMA_QUERY_ENGINE_LIBRARY:
        '/opt/nodejs/node_modules/@prisma/engines/libquery_engine-rhel-openssl-3.0.x.so.node',
    };

Then in my function props, I only copy the generated client code (and the schema), skipping the engine files:

// stacks/layers.ts

app.setDefaultFunctionProps({
    copyFiles: [
      { from: 'node_modules/.prisma/client/index.js', to: '.prisma/client/index.js' },
      { from: 'node_modules/.prisma/client/index.d.ts', to: '.prisma/client/index.d.ts' },
      { from: 'node_modules/.prisma/client/package.json', to: '.prisma/client/package.json' },
      { from: 'node_modules/.prisma/client/schema.prisma', to: '.prisma/client/schema.prisma' },
  ]

  // ... rest of props ...

});

@revmischa
Copy link
Member

I don't think the copyFiles bit should be needed because esbuild should be bundling it with your application.

In my project I'm using graviton (ARM) lambdas so I'm using the library:

            "/opt/nodejs/node_modules/@prisma/engines/libquery_engine-linux-arm64-openssl-3.0.x.so.node",

I didn't have to specify the client arch to generate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants