nuxt/framework

Nuxt 3 nitro + mikro-orm

max5432112345 opened this issue · 5 comments

Environment

  • Operating System: Linux
  • Node Version: v16.15.1
  • Nuxt Version: 3.0.0-rc.4
  • Package Manager: npm@8.12.2
  • Builder: vite
  • User Config: -
  • Runtime Modules: -
  • Build Modules: -

Reproduction

import { RequestContext } from '@mikro-orm/core'

import { DI } from '../data-source'

export default defineEventHandler(async (event) => {
  console.log(next)
  RequestContext.create(DI.orm.em, next) // no next function
})

OR

import { RequestContext } from '@mikro-orm/core'

import { DI } from '../data-source'

export default (req, res, next) => {
  console.log(next)
  RequestContext.create(DI.orm.em, next) // has next but not work, such error was with fastify https://github.com/fastify/fastify/issues/3570
}

DI file

import 'reflect-metadata'
import {
  // EntityManager,
  // EntityRepository,
  MikroORM
} from '@mikro-orm/core'

import { Author } from './entities'

export const DI = {} as {
  orm: MikroORM,
  // em: EntityManager,
  // authorRepository: EntityRepository<Author>
}

(async () => {
  DI.orm = await MikroORM.init({
    type: 'mariadb',
    entities: [Author],
    dbName: 'nuxt',
    password: '123456',
    user: 'admin',
    debug: true
  })
})()

Describe the bug

No next function. In legacy exist but no effect like with old version of fastify here
nestjs/nest#8837
nodejs/node#41285

[nitro] [dev] [unhandledRejection] ValidationError: Using global EntityManager instance methods for context specific actions is disallowed. If you need to work with the global instance's identity map, use allowGlobalContext configuration option or fork() instead.

Additional context

No response

Logs

No response

need useNext() helper???

+1 Stuck at this too

@max5432112345 here a approach that works for me, but is defenitly not the end-solution:

Create a server middleware with following content which initialize the database connection if no one exists:

import { RequestContext } from '@mikro-orm/core'
import { useClient } from '../datasources/tenant/client'

export default defineEventHandler(async (event) => {
  const client = await useClient();

  // TODO:: https://github.com/nuxt/framework/issues/5603
  RequestContext.create(client.orm.em, () => { })

  event.context.tenantClient = client;
})

A client file:

import type { MySqlDriver } from '@mikro-orm/mysql';
import { MikroORM, EntityRepository, EntityManager } from "@mikro-orm/core";
import { Author, Book, BookTag, Publisher, BaseEntity } from "./entities";
import { SqlHighlighter } from "@mikro-orm/sql-highlighter";

export type Client = {
    orm: MikroORM,
    em: EntityManager,
    authorRepository: EntityRepository<Author>
};

let client: Client;

export const useClient = async () => {
    if (!client) {
        // TODO:: CLI config will be used automatically
        const orm = await MikroORM.init<MySqlDriver>({
            dbName: "mikro-orm",
            type: "mysql",
            host: process.env.MYSQL_HOST || "127.0.0.1",
            port: Number(process.env.MYSQL_PORT) || 3306,
            user: process.env.MYSQL_USERNAME || "root",
            password: process.env.MYSQL_PASSWORD || "root",
            // as we are using class references here, we don't need to specify `entitiesTs` option
            entities: [Author, Book, BookTag, Publisher, BaseEntity],
            highlighter: new SqlHighlighter(),
            discovery: { disableDynamicFileAccess: true },
            debug: true, // todo:: env
        });
        const em = orm.em.fork();
        const authorRepository = em.getRepository(Author);

        client = {
            orm, em, authorRepository
        };
    }

    return client;
}

Now you can use in your /api/foo.ts api endpoint the client (orm) as following:

import { QueryOrder } from "@mikro-orm/core";
import { Client } from "../datasources/tenant/client";

export default defineEventHandler(async (event) => {
    const { tenantClient } = event.context as { tenantClient: Client };

    const authors = await tenantClient.authorRepository.findAll({
        populate: ['books'],
        orderBy: { name: QueryOrder.DESC },
        limit: 20,
    });

    return {
        statusCode: 200,
        body: JSON.stringify(authors),
    };
});

Hope it helps you a bit