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

How to use it with MongoDB Atlas? #31

Closed
ghost opened this issue Apr 2, 2019 · 3 comments
Closed

How to use it with MongoDB Atlas? #31

ghost opened this issue Apr 2, 2019 · 3 comments

Comments

@ghost
Copy link

ghost commented Apr 2, 2019

I am trying to use mikro-orm with MongoDB Atlas, but I am getting a permissions error.

{ MongoError: user is not allowed to do action [find] on [debug.business]
    at Connection.<anonymous> (/home/alejandro/Proyectos/localeco/backend/node_modules/mongodb-core/lib/connection/pool.js:443:61)
    at Connection.emit (events.js:189:13)
    at processMessage (/home/alejandro/Proyectos/localeco/backend/node_modules/mongodb-core/lib/connection/connection.js:364:10)
    at TLSSocket.<anonymous> (/home/alejandro/Proyectos/localeco/backend/node_modules/mongodb-core/lib/connection/connection.js:533:15)
    at TLSSocket.emit (events.js:189:13)
    at addChunk (_stream_readable.js:284:12)
    at readableAddChunk (_stream_readable.js:265:11)
    at TLSSocket.Readable.push (_stream_readable.js:220:10)
    at TLSWrap.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
  ok: 0,
  errmsg:
   'user is not allowed to do action [find] on [debug.business]',
  code: 8000,
  codeName: 'AtlasError',
  name: 'MongoError',
  [Symbol(mongoErrorContextSymbol)]: {} }

I think I have followed the documentation correctly. Here the relevant code.

index.ts

import { Collection, MongoClient, ObjectId } from "mongodb";
import "reflect-metadata";
import { container } from "tsyringe";
import OrmClient from "./ormClient";
import WebServer from "./webServer";

async function bootstrap(): Promise<void> {
    const ormClient: OrmClient = container.resolve(OrmClient);
    const webServer: WebServer = container.resolve(WebServer);

    await ormClient.initialize();
    await webServer.initialize();

    await test();
}

async function test(): Promise<void> {
    const mongo: MongoClient = new MongoClient(process.env.DATABASE_URL as string, {
        auth: {
            user: process.env.DATABASE_USER as string,
            password: process.env.DATABASE_PASSWORD as string,
        },
        useNewUrlParser: true,
    });

    await mongo.connect();

    const collection: Collection = mongo.db(process.env.DATABASE_NAME as string).collection("business");

// tslint:disable-next-line: no-console
    console.log(await collection.findOne({ _id: ObjectId.createFromHexString("5c9d155106dd7215cee1da4c")}));
}

export default bootstrap();

ormClient.ts

import { EntityManager, MikroORM } from "mikro-orm";
import { singleton } from "tsyringe";

@singleton()
export default class OrmClient {
    private mikroOrm!: MikroORM;

    public async initialize(): Promise<void> {
        this.mikroOrm = await MikroORM.init({
            clientUrl: process.env.DATABASE_URL as string,
            user: process.env.DATABASE_USER as string,
            password: process.env.DATABASE_PASSWORD as string,
            dbName: process.env.DATABASE_NAME as string,
            entitiesDirs: ["build/entities"],
            entitiesDirsTs: ["source/entities"],
        });
    }

    public get em(): EntityManager {
        return this.mikroOrm.em;
    }
}

entities/business.ts

import { Entity, IEntity, PrimaryKey, Property } from "mikro-orm";
import { ObjectId } from "mongodb";

@Entity()
export class Business {
    @PrimaryKey()
    public _id!: ObjectId;

    @Property()
    public name!: string;

    @Property()
    public description!: string;
}

export interface Business extends IEntity<string> { }

webServer.ts

import express, { Application } from "express";
import { createServer, Server } from "http";
import { injectable } from "tsyringe";
import GraphqlApi from "./middlewares/graphqlApi";
import OrmContext from "./middlewares/ormContext";

@injectable()
export default class WebServer {
    private application: Application;
    private server: Server;

    public constructor() {
        this.application = express();
        this.server = createServer(this.application);
    }

    public async initialize(): Promise<void> {
        const ormContext: OrmContext = new OrmContext();
        const graphqlApi: GraphqlApi = new GraphqlApi();

        ormContext.apply(this.application);
        graphqlApi.apply(this.application);

        await this.listen();
    }

    ...

middlewares/ormContext.ts

import { Application } from "express";
import { RequestContext } from "mikro-orm";
import { autoInjectable, inject } from "tsyringe";
import OrmClient from "../ormClient";

@autoInjectable()
export default class OrmContext {
    private ormClient: OrmClient;

    public constructor(@inject(OrmClient) ormClient?: OrmClient) {
        this.ormClient = ormClient as OrmClient;
    }

    public apply(application: Application): void {
        application.use((...[, , next]) => {
// tslint:disable-next-line: no-console
            console.log("PASO");
            RequestContext.create(this.ormClient.em, next);
        });
    }
}

providers/businessStore.ts

import { DataSource } from "apollo-datasource";
import { autoInjectable, inject } from "tsyringe";
import { Business } from "../entities/business";
import OrmClient from "../ormClient";
import { GraphqlContext } from "../types/common";

@autoInjectable()
export default class BusinessStore extends DataSource<GraphqlContext> {
    private ormClient: OrmClient;

    public constructor(@inject(OrmClient) ormClient?: OrmClient) {
        super();

        this.ormClient = ormClient as OrmClient;
    }

    public async getId(id: string): Promise<any> {
// tslint:disable-next-line: no-console
        console.log(id);

        try {
            return await this.ormClient.em.findOne(Business, "5c9d155106dd7215cee1da4c");
        } catch (error) {
// tslint:disable-next-line: no-console
            console.log(error);
            return null;
        }
    }
}

And the output of start the application and do one request:

{ _id: 5c9d155106dd7215cee1da4c,
  name: 'Prueba',
  description: 'Prueba de creaccion' }
PASO
5c9d155106dd7215cee1da4c
{ MongoError: user is not allowed to do action [find] on [debug.business]
    at Connection.<anonymous> (/home/alejandro/Proyectos/localeco/backend/node_modules/mongodb-core/lib/connection/pool.js:443:61)
    at Connection.emit (events.js:189:13)
    at processMessage (/home/alejandro/Proyectos/localeco/backend/node_modules/mongodb-core/lib/connection/connection.js:364:10)
    at TLSSocket.<anonymous> (/home/alejandro/Proyectos/localeco/backend/node_modules/mongodb-core/lib/connection/connection.js:533:15)
    at TLSSocket.emit (events.js:189:13)
    at addChunk (_stream_readable.js:284:12)
    at readableAddChunk (_stream_readable.js:265:11)
    at TLSSocket.Readable.push (_stream_readable.js:220:10)
    at TLSWrap.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
  ok: 0,
  errmsg:
   'user is not allowed to do action [find] on [debug.business]',
  code: 8000,
  codeName: 'AtlasError',
  name: 'MongoError',
  [Symbol(mongoErrorContextSymbol)]: {} }

As you can see using the MongoDB native driver with same credentials all work well.

@B4nan
Copy link
Member

B4nan commented Apr 2, 2019

Hello there!

MongoClient currently supports only clientUrl and dbName options for this, others like user, password or port are ignored (they were introduced later with SQL drivers).

Try to construct the clientUrl to include user and password, that should help. I will create another issue to support overriding connection options in mongo driver.

You can put your user and password in the first part of connection string like this:

const clientUrl = 'mongodb://${user}:${password}@${host}';

@ghost
Copy link
Author

ghost commented Apr 2, 2019

Yea, solved. Thanks you.

@ghost ghost closed this as completed Apr 2, 2019
@B4nan
Copy link
Member

B4nan commented Apr 5, 2019

Your original approach should be now working as well in v2.1

This issue was closed.
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

1 participant