question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Support passing custom metadata per query to middleware

See original GitHub issue

Problem

Currently the middleware are essentially stateless. There is one example that uses global state but in practice this is not very common nor desirable. It would be necessary for a tracing middleware.

Suggested solution

It would be nice if all prisma calls add a context parameter that would be passed down to middlewares. It could look like:

export type TicketCreateArgs = {
    select?: TicketSelect | null
    include?: TicketInclude | null
    data: XOR<TicketCreateInput, TicketUncheckedCreateInput>
    context: Record<string, any>
}
export type MiddlewareParams = {
    model?: ModelName
    action: PrismaAction
    args: any
    dataPath: string[]
    runInTransaction: boolean
    context: Record<string, any>
}

Alternatives

No good alternative currently if you want to respect typing, unsure if I could pass the context in the data and remove it in the middleware, but I would prefer not too.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:30
  • Comments:23 (11 by maintainers)

github_iconTop GitHub Comments

32reactions
lorenzobersanocommented, Sep 27, 2022

Any news about this feature? I would love it in order to cleanly implement audit trails using Prisma Middlewares 🙂

26reactions
matthewmuellercommented, Aug 18, 2022

Hey folks, we started speccing this out this and we’re ready to share our proposal. Please let us know what you think! I’d also like to invite you to jump on a quick call with me to get your thoughts on the proposal.

Overview

Today Prisma users can’t selectively opt-in or out of middleware functionality.

If we allowed developers to turn on certain middleware per-query, we could make middleware more useful because the middleware would be able to make decisions based on the context the query was made.

Use Cases

Goals and Product Requirements

  • You can pass arbitrary data per query to middleware
  • The metadata is modifiable along the middleware chain
  • Nice to have: There should be some way for users to make it type-safe.
    • Sharing a user-defined type across files would be fine.

Out of Scope

  • Automatically passing context to multiple queries: We can potentially address this with a feature like Model Scopes, but for now you need to pass metadata per query.

Open Questions

  • How would this affect transaction rollbacks?
  • Would middleware be the only part of the API that has access to this metadata?
  • Is there a use case for global metadata? (PR includes this support)

Approach

Add support for a meta field that can to be passed through all of our query objects code. This meta field would be accessible through the middleware stack:

// include a password only when explicitly passed in
prisma.$use(async (params, next) => {
  const result = await next(params);

  // TODO: is there a way to make "params.meta" type-safe?
  if (params.model === "User" && !params.meta.includePassword) {
    const { password, ...rest } = result;
    return rest;
  }

  return result;
})

// encrypt by default
prisma.$use(async (params, next) => {
  if (params.model !== "User") {
    return next(params)
  }
  // encrypt by default
  if (params.action == "create") {
    if (args.data["secret"]) {
      args.data["secret"] = encrypt(secretKey, args.data["secret"])
    }
  }
  // pass along
  const result = await next(params);
  // return encrypted unless
  if (params.meta.decrypt) {
    result["secret"] = decrypt(secretKey, result["secret"])
  }
  return result
})

All APIs that can be modified by middleware would support this meta field:

// Omit passwords unless includePassword is true
prisma.user.findMany({
  where: {
    email: "alice@gmail.com"
  },
  meta: {
    includePassword: true
  },
})

// Encrypt secrets unless decrypt is true
prisma.user.findMany({
  where: {
    email: "alice@gmail.com"
  },
  meta: {
    decrypt: true
  },
})

// Create an encrypted user that gets returned back encrypted
prisma.user.create({
  data: {
    name: "Alice",
    email: "alice@gmail.com",
    secret: "my dark secret",
  },
  meta: {
    decrypt: false
  },
})

// Yes, even groupBy!
prisma.user.groupBy({
  by: ["email"]
  meta: {
    decrypt: true
  },
})

Type of basic meta field is defined in Prisma namespace as:

interface Meta {
   [key:string]: unknown.
}

Additionally, we define a meta type for all of the actions, for example:

interface FindFirstMeta extends Meta {
}

Each action method signature will use it’s corresponding meta type.

This allows meta property to accept any data without type-checking. If users want to have a stricter type checking for the meta fields that make sense for their application, they have an option to augment provided interfaces in their codebases. For example, decrypt middleware can ensure strict type-checking for decrypt prop by declaring:

declare module "@prisma/client" {
  export namespace Prisma {
      export interface Meta {
        decrypt?: boolean;
     }
  }
}

If certain option makes sense only for specific action, users have an option to augment only this actions interface. For example, soft delete middleware could do:

declare module "@prisma/client" {
  export namespace Prisma {
      export interface DeleteMeta {
        soft?: boolean;
     }
  }
}

References

Read more comments on GitHub >

github_iconTop Results From Across the Web

Adding Custom Metadata for Oracle WebCenter Content Server
In the data model editor task pane, click Custom Metadata. · Oracle WebCenter Content Server stores metadata under a document profile. · Click...
Read more >
API Reference: ApolloServer - Apollo GraphQL Docs
This article documents the ApolloServer class from the @apollo/server package. You can use the ApolloServer class to create an instance of Apollo Server ......
Read more >
Custom Metadata Allocations and Usage Calculations
* Record size is based on the maximum field size of each field type, not the actual storage that's used in each field....
Read more >
Metadata and Docs URLs - FastAPI
You can customize several metadata configurations in your FastAPI application. ... terms_of_service, str, A URL to the Terms of Service for the API....
Read more >
How to extend the Express Request object in TypeScript
Prerequisites; Adding custom properties to the Request type ... query : this object contains a property for each query string parameter ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found