Last active
May 25, 2024 04:38
-
-
Save queerviolet/41c01c6b7b5198f688c0b6e10523e735 to your computer and use it in GitHub Desktop.
Revisions
-
queerviolet revised this gist
Nov 21, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -49,7 +49,7 @@ export const TransactionPlugin = <Txn>(transact: Transact<Txn>): ApolloServerPlu transactionResult = transact(t => { resolve(t) // We return our didFinish promise here to hold the // transaction open while resolvers are executing. return didFinish }).then(() => ({ ok: true }), error => ({ error })) -
queerviolet revised this gist
Nov 21, 2019 . 1 changed file with 3 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -48,6 +48,9 @@ export const TransactionPlugin = <Txn>(transact: Transact<Txn>): ApolloServerPlu // warnings from node. transactionResult = transact(t => { resolve(t) // We return our didFinish promises here to hold the // transaction open while resolvers are executing. return didFinish }).then(() => ({ ok: true }), error => ({ error })) ) -
queerviolet revised this gist
Nov 21, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -40,7 +40,7 @@ export const TransactionPlugin = <Txn>(transact: Transact<Txn>): ApolloServerPlu // Create the transaction. // // The db.transact function may create a transaction and call its // callback asynchronously—this deals with that correctly. // `transaction` captures the (promise of the) transaction. const transaction: Promise<Txn> = new Promise((resolve) => // We capture the result of this transaction here, attaching -
queerviolet revised this gist
Nov 21, 2019 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -78,7 +78,7 @@ export const TransactionPlugin = <Txn>(transact: Transact<Txn>): ApolloServerPlu // transactionResult. If this is a failing promise (i.e., the // transaction could not be committed), the entire // request will fail with an error. return transactionResult .then(({ error }) => { throw error }) } } -
queerviolet revised this gist
Nov 21, 2019 . 2 changed files with 2 additions and 4 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,8 +4,7 @@ * use them in Apollo Server: */ import { ApolloServerPlugin, GraphQLRequestContext } from 'apollo-server-plugin-base' export interface Transactable<Txn> { transact?: Transact<Txn> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,4 @@ import { ApolloServerPlugin, GraphQLRequestContext } from 'apollo-server-plugin-base' /** * If your database provides imperative transaction (with separate create, -
queerviolet revised this gist
Nov 21, 2019 . 1 changed file with 51 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,51 @@ import { ApolloServerPlugin } from "src/plugins"; import { GraphQLRequestContext } from "apollo-server-plugin-base"; /** * If your database provides imperative transaction (with separate create, * commit, and rollback methods), here's how you can use that in Apollo * Server: */ export interface TxnControls<Txn> { create(): Promise<Txn> commit(txn: Txn): Promise<void> rollback(txn: Txn): void } export interface Transactable<Txn> { transact?: Transact<Txn> } export type Transact<Txn> = <T>(block: TransactionBlock<Txn, T>) => Promise<T> export type TransactionBlock<Txn, T> = (txn: Txn) => Promise<T> export const ImperativeTransactionPlugin = <Txn>(controls: TxnControls<Txn>): ApolloServerPlugin => ({ requestDidStart<T extends Transactable<Txn>>(requestContext: GraphQLRequestContext<T>) { let transactionResult: Promise<{ ok?: boolean, error?: any }> | void = undefined return { executionDidStart() { const txn = controls.create() requestContext.context.transact = async (block: (txn: Txn) => any) => block(await txn) return (err) => txn .then(t => err ? controls.rollback(t) : controls.commit(t)) .then(() => ({ ok: true }), error => ({ error })) }, willSendResponse() { // If execution never started, presumably some other error occurred // and will be reported in its own way. if (!transactionResult) return // Finally, in willSendResponse, we return the (promise of the) // transactionResult. If this is a failing promise (i.e., the // transaction could not be committed), the entire // request will fail with an error. return Promise.resolve(transactionResult) .then(({ error }) => { throw error }) } } } }) -
queerviolet revised this gist
Nov 21, 2019 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,10 +1,12 @@ /** * If your database provides block transactions, (like Sequelize, * here: https://sequelize.org/master/manual/transactions.html) here's how to * use them in Apollo Server: */ import { ApolloServerPlugin } from "src/plugins" import { GraphQLRequestContext } from "apollo-server-plugin-base" export interface Transactable<Txn> { transact?: Transact<Txn> } -
queerviolet created this gist
Nov 21, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,85 @@ /** * If your database provides block transactions, (like Sequelize, * here: https://sequelize.org/master/manual/transactions.html) here's how to * use them in Apollo Server: */ export interface Transactable<Txn> { transact?: Transact<Txn> } export type Transact<Txn> = <T>(block: TransactionBlock<Txn, T>) => Promise<T> export type TransactionBlock<Txn, T> = (txn: Txn) => Promise<T> export const TransactionPlugin = <Txn>(transact: Transact<Txn>): ApolloServerPlugin => ({ requestDidStart<T extends Transactable<Txn>>(requestContext: GraphQLRequestContext<T>) { // transactionResult is going to track the eventual result of our // db transaction. // // It may remain undefined if execution never started—i.e. because // parsing or validation failed. // // We need to attach error handlers after the transaction may have failed, // so we track the result or failure as a value (that is, // transactionResult will never reject). let transactionResult: Promise<{ ok?: boolean, error?: any }> | void = undefined return { executionDidStart() { // didFinish is going to get resolved after graphQL execution is done, // thereby commiting the transaction. let ok: (value?: unknown) => void, fail: (reason?: any) => void const didFinish = new Promise((resolve, reject) => { ok = resolve fail = reject }) // Create the transaction. // // The db.transact function may create a transaction and call its // callback asynchonrously—this deals with that correctly. // `transaction` captures the (promise of the) transaction. const transaction: Promise<Txn> = new Promise((resolve) => // We capture the result of this transaction here, attaching // error handlers immediately to avoid UnhandledPromiseError // warnings from node. transactionResult = transact(t => { resolve(t) return didFinish }).then(() => ({ ok: true }), error => ({ error })) ) // Add a transaction utility to the context. // This behaves exactly like the `db.transact` provided by the database: // You do your queries, passing in the request handle, and making // sure to chain and return them as appropriate. requestContext.context.transact = async <T,>(block: (txn: Txn) => Promise<T>): Promise<T> => block(await transaction) // Finally, in the executionDidFinish callback, we either resolve // or reject the didFinish promise. This commits or rolls back the // transaction, respectively. return (err) => { if (err) fail(err) ok() } }, willSendResponse() { // If execution never started, presumably some other error occurred // and will be reported in its own way. if (!transactionResult) return // Finally, in willSendResponse, we return the (promise of the) // transactionResult. If this is a failing promise (i.e., the // transaction could not be committed), the entire // request will fail with an error. return Promise.resolve(transactionResult) .then(({ error }) => { throw error }) } } } })