Skip to content
This repository has been archived by the owner on Sep 2, 2022. It is now read-only.

Raw database access fallback #2052

Closed
schickling opened this issue Mar 9, 2018 · 7 comments
Closed

Raw database access fallback #2052

schickling opened this issue Mar 9, 2018 · 7 comments

Comments

@schickling
Copy link
Member

schickling commented Mar 9, 2018

Introduction

Prisma exposes a flexible API that conforms to the OpenCRUD specification.

This API has excellent developer ergonomics and is powerful enough to cover most use cases. Sometimes, however you need to drop down to the native API of the underlying database to performa a more complex operation. This ticket specifies how raw database access can be exposed in the existing GraphQL API.

API

Considerations:

  • As an arbitrary query can modify data, it should be a mutation and the name should suggest that it is an imperative action.
  • It must be possible to select which database to execute the query against
  • Different kinds of databases (MySQL, MongoDB) should be supported

The two following API extensions are proposed:

Option A: Nested input

mutation {
  executeRaw({
    default: "SELECT TOP(10) FROM Users ORDER BY uuid()"
  })
}

Option B: Enum based

mutation {
  executeRaw(
    database: default,
    query: "SELECT TOP(10) FROM Users ORDER BY uuid()"
  )
}

Option B should have database be an optional argument.

Unless there are good arguments against it, we will go with option B

Databases

SQL

SQL databases are queried using a SQL string:

"SELECT * FROM Users ORDER BY uuid() Limit 2"

Return value is an array of objects:

[{
  name: "Carl"
  id: 1
},
{
  name: "Caroline",
  id: 2
}]

To return multiple result sets, simply use aliases to perform multiple executeRaw mutations

MongoDB

MongoDB is queried using a string containing a normal MongoDB query operation:

"db.users.find().limit(2)"

Return value is an array of objects:

[{
  name: "Carl"
  id: 1
},
{
  name: "Caroline",
  id: 2
}]

Some mongo queries return a single object. In that case the single object is wrapped in an array.

Elasticsearch

Elastic is queried using the normal query DSL:

"{
  \"query\": { \"match_all\": {} }
}"

Note that the query itself does not specify the collection to search. Normally this is determined by the URL that is being queried, ie /user/_search. We will need an extra field in the GraphQL API to specify the collection:

mutation {
  executeRaw(
    database: default,
    query: "{\"query\": { \"match_all\": {} }}",
    collection: "user"
  )
}

And returns an array of objects:

[{
  name: "Carl"
  id: 1
},
{
  name: "Caroline",
  id: 2
}]

Redis

Redis supports a large set of commands, for example scan:

"scan 0"

Return set is an array containing either nested arrays, objects or scalar values depending on the command:

[
  17,
  ["key:12", "kay:8"]
]

Notes

Also see https://www.youtube.com/watch?v=YUjlBuI8xsU

Configuration

This feature significantly widens the database access given to users of a Prisma service. Therefore, it should be possible to configure the feature to limit access or completely disable it.

Disable completely

If the feature is disabled for all databases, the executeRaw mutation is completely removed from the GraphQL API.

In the databases section of the PRISMA_CONFIG you can set the field rawAccess to false to disable raw database access. If the field is not specified it defaults to false.

PRISMA_CONFIG: |
        managementApiSecret: my-server-secret-123
        port: 4466
        databases:
          default:
            rawAccess: false
            connector: mysql
            migrations: true
            host: mysql-db
            port: 3306
            user: root
            password: prisma

Fine-grained control

Any query run through the executeRaw mutation is executed with the privileges of the database user configured for the connector. This includes the ability to access a schema belonging to a different service in a multi-tenant prisma installation. We should explore how we can allow raw database access while limiting the scope to the same service.

Prisma Client

The prisma client is implemented in several different languages. This describes the Typescript implementation and other languages will have a similar API.

API

To execute a raw query you need to specify what database to query, as well as the actual query. The following is the method definition for querying the default database:

prisma.$raw.default(query: String): Promise<Array<any>>

For convenience, the default database can also be queried directly:

prisma.$raw(query: String): Promise<Array<any>>

The method interface can differ for different types of databases. For example a Elastic Search database requires you to specify the collection as well as the query:

prisma.$raw.mySearchDatabase(collection: String, query: String): Promise<Array<any>>

Note: we could decide to provide a JsonArray interface as described in microsoft/TypeScript#1897 (comment)

Example

prisma.$raw("SELECT * FROM user LIMIT 1")

returns

[{
  id: 1,
  name: "Søren",
  age: 42
}]
@rostislav-simonik
Copy link

rostislav-simonik commented Apr 19, 2018

Hello, I'd like ask you what the man can imagine under given issue?
Something similar as prisma-bindings or interface for native prisma extensions.

Very thanks for explaining the initial intension.

@mavilein
Copy link
Contributor

The executeRaw mutation is now available in 1.17.0-beta, which was just published now.

@mcmar
Copy link

mcmar commented Oct 11, 2018

Hi, I noticed the examples given here as well as in the release notes don't actually work due to this bug: prisma/prisma#3300
Please see the bug and determine if fixing that is something that you'd like to support.

@mavilein mavilein closed this as completed Nov 2, 2018
@terion-name
Copy link

Why does this api is not exposed in "prisma-binding"?

@sorenbs
Copy link
Member

sorenbs commented Nov 21, 2018

@terion-name - thanks for opening the issue in prisma-binding! It would be great for somebody in the community to open a pull-request adding support.

Keep in mind that we have an upcoming addition to add support for parameter interpolation. It might be worth it to wait for this change.

cc @maticzav

@maticzav
Copy link
Contributor

@sorenbs I believe prisma-binding should work without any further changes. Why do you think we would have to change anything?

@terion-name
Copy link

terion-name commented Nov 23, 2018

@maticzav well I've checked - bindings do not expose mutation.executeRaw with new Prisma.

UPD
Saw your comment in other issue. It's strange, maybe I've done something wrong, need to check once more

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

No branches or pull requests

8 participants