thebigredgeek / apollo-resolvers Goto Github PK
View Code? Open in Web Editor NEWExpressive and composable resolvers for Apollostack's GraphQL server
License: MIT License
Expressive and composable resolvers for Apollostack's GraphQL server
License: MIT License
Hey @thebigredgeek
Congratulations for this nice work. I want to authenticate the incoming requests using apollo-resolvers. Please help me to get a sample code.
Thanks
Is there any sample implementation available ?I am not able resolve userModel
Hi. I recently used this library and really enjoy it. I wonder if there's anyway if we can pass data from a resolver to another.
For example, let's say I have a resolver isStatusOwnerResolver
like this:
export const isStatusOwnerResolver = isAuthenticatedResolver.createResolver(
async (root, { statusId }, { userId, models: { Status } }) => {
const status = await Status.findById(statusId)
if (status.ownerId.toString() !== userId) {
throw new NotStatusOwnerError()
}
},
)
Is there anyway I can pass status
to the resolver that is chained from isStatusOwnerResolver
?
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates are currently rate-limited. Click on a checkbox below to force their creation now.
These updates have been manually edited so Renovate will no longer make changes. To discard all commits and start over, click on a checkbox.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
.circleci/config.yml
circleci/node 8
circleci/node 10
circleci/node 12
circleci/node 14
package.json
assert ^2.0.0
deepmerge ^3.0.0
babel-cli 6.26.0
babel-core 6.26.3
babel-eslint 7.2.3
babel-plugin-transform-object-rest-spread 6.26.0
babel-preset-env 1.7.0
babel-register 6.26.0
bluebird 3.5.4
chai 3.5.0
eslint 3.19.0
eslint-plugin-babel 3.3.0
express 4.16.4
graphql-server-express 0.6.0
mocha 3.5.3
rimraf 2.6.3
sinon 1.17.7
supertest 3.4.2
typescript 2.9.2
typings 2.1.1
Hello there!
This is what I did in order to handle database connection errors using MongoDB and 2 awesome packages
apollo-errors
and apollo-resolvers
.
First of all I created a middleware that handles the connection to MongoDB where the client
is a promise that resolves to the Connected client and the db
is the database instance.
Note that I am calling the next middleware even though there is an error or not and the same time attaching the client and the db instance to request object in order to be available in the context object, here is where I'm not sure if is safe or bad doing it in this way because in my case I'm doing a signup form and well I need to open a connection to the database every time the user makes a request.
const dbConnection = async (req, res, next) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017')
const db = client.db('myproject')
req.client = client
req.db = db
next()
} catch (error) {
req.err = error.message
next()
}
}
app.use('/graphql', dbConnection, bodyParser.json(), graphqlExpress(req => {
return {
schema,
formatError,
context: {
client: req.client,
db: req.db,
err: req.err
}
}
}))
Then I create a base resolver for unknown thrown errors and another one that get all the users from the database, note that in the last resolver I am checking if there is an error, again I don't know if is good or bad doing it this way.
const baseResolver = createResolver(
null,
(_, args, context, error) => {
if (isInstance(error)) return error
return new UnknownError({
data: {
message: 'Oops! Something went wrong'
}
})
})
const getAllUsers = baseResolver.createResolver(
(_, args, {
client,
db,
err
}) => {
if (err) throw new DatabaseConnectionError({
data: {
message: err
}
})
return ...
})
const resolvers = {
Query: {
getAllUsers
},
I could do this directly in a resolver called dbConnectionResolver
and throw erros from there and spam getAllUsers
from the previous one like so: const getAllUsers = dbConnectionResolver.createResolver(...)
the problem though is if there is no errors thrown in the dbConnectionResolver
I don't get access to the client
and the db
instance in the next resolver.
Surely I'm doing something wrong because of my lack of knowledge if somebody have some thoughts on this I'll appreciate it. Thanks.
As best as I can tell, this implementation of resolvers does not pass along the info
argument from Apollo's resolver signature. This breaks my usage of fieldASTs
to skip unnecessary DB joins in the resolvers.
This would be a breaking change, but would you consider changing your resolver signature to match Apollo's? My use case represents only one of the many useful things that can be done with info
.
resolver(root, args, context, info, errors) {}
Hey lib is great! Have a question regarding errors thrown. Can you provide an example of handling the errors, I'd like to catch them before it is passed to the client and I'm struggling to find best way.
Thanks,
ron
On building with typescript, this error message is presented
error TS2339: Property 'createResolver' does not exist on type '(root: any, args?: {}, context?: {}) => Promise<any>'.
Is there anything that needs to be updated?
I've reverted to 1.0.3 for now
Epic library!
The problem is this. When my custom base resolver has handled the error and returns it, it is thrown which leads to an unhandled error in the runtime.
Is it necessary to catch the error in the endpoint resolver? In that case, shouldn't there be an option not to throw the error?
For example, if I have an authResolver, I don't want to have to catch the UnauthorizedError every time I create a resolver with it.
Or is there any other best practise of this?
EDIT
Turns out I've misunderstood the graphql flow. It was graphql itself that threw the error so there cannot be an option for this. To get rid of the thrown error set graphqlOptions.debug = false
All of them can be worked around with any
and !
, but it would be nice to see them fixed here.
First one:
export interface CreateResolverFunction {
<R, E>(resFn: ResultFunction<R>, errFn?: ErrorFunction<E>): Resolver<R>
}
export const createResolver: CreateResolverFunction
Probably resFn
should be optional or nullable, because in your examples you use it like this:
export const baseResolver = createResolver(
//incoming requests will pass through this resolver like a no-op
null,
/*
Only mask outgoing errors that aren't already apollo-errors,
such as ORM errors etc
*/
(root, args, context, error) => isInstance(error) ? error : new UnknownError()
);
Or maybe there should be two types, one for module-level createResolver
and one for Resolver
's createResolver
Second is that you define Resolver as
export interface Resolver<ResulType> {
(root, args: {}, context: {}, info: {}): Promise<ResulType>
createResolver?: CreateResolverFunction
compose?: ComposeResolversFunction
}
As far as I get it, the only way to create resolver is via createResolver
and it will always add createResolver
and compose
, so they shouldn't be optional in interface.
Is it expected that returning null from a resolver should allow the request to continue to the child resolver?
Since null is a valid return type for nullable fields, it may be desired that a resolver returns an early null like so.
eg.
const isAuthenticatedResolver = baseResolver.createResolver(
(root, args, { isAuthenticated }) => {
if (!isAuthenticated) return null;
}
);
I got this error because info not passed to resolver. :(
TypeError: Cannot read property 'parentType' of undefined
on graphql-relay globalIdResolver
(obj, args, context, info) => toGlobalId(
typeName || info.parentType.name,
idFetcher ? idFetcher(obj, context, info) : obj.id
)
Hi, i'm right now studying those themes mentioned above. That's the question (above).
I'm creating a simple GraphQL API structure to learn about it. And I wish to integrate it with Firebase Auth. Couse I'm studying about the Auth from FB to use in a React Native client.
And I'm venturing into Restify xD
Many thanks,
Best Regards.
It would appear that the dist
file index.js
is missing everything except combineResolvers
and usePromise
. This is the case for 1.0.0 and 1.0.1. That makes the library unusable via NPM or Yarn.
I'm guessing it could be something in the build process.
Can you please add the 4th parameter to createResolver. The info object contains some useful information when attempting to create more advanced resolvers.
Hi, funny thing. Not all cases are covered in tests and there is a bug with one of these.
Here is an example of test with or
condition:
// test/unit/helper_spec.js
it('when (true, false) and resolver throws, it should return exactly that error', () => {
const resolver = or(successResolver, failureResolver);
const testError = new Error('test');
const finalResolver = resolver(() => {
throw testError;
});
return finalResolver()
.catch(err => {
expect(err).to.equal(testError);
});
});
So, if first resolver succeed it immediately runs query. But if query throws, second resolver runs after that and if it is throws, we receive that second error, but not the error that query throws
I'm wondering what's the best approach to achieve the following behaviour:
1st resolver: Checks if user is authenticated
2nd resolver: Checks if user has permissions to edit the document. Since we already fetched the document in this step to check permissions, it would be nice to be able to pass the document to the next resolver.
3rd resolver: Query or make the changes to the document in question.
This is what my resolvers look like so far:
const isAuthenticatedResolver = createResolver((root, args, { user }) => {
if (!user) throw new UnauthenticatedError();
});
const isInOrganization = isAuthenticatedResolver.createResolver(
(root, { organizationId }, { user }) => {
return Organization.findOne({ _id: organizationId, owner: user.id })
.then(organization => {
if (!organization) throw new CustomError('Please check you have permission to access this organization');
// How do I pass organization to the next stage?
// I tried `return`ing organization at this point, but that resolves[?] the resolver and prevents
// the next one to be executed.
});
}
);
const RootResolver = {
getMembers: isInOrganization.createResolver(
(organization) => organization.getMembers()
)
}
Hi,
is it possible to use apollo-resolvers as subscription resolver?
I noticed in this case the context is not passed to resolver.
export const resolvers = {
//...
Subscription: {
status: {
subscribe:
isAuthenticatedResolver.createResolver(
(_, args, context, info) => {
//...
}
),
resolve: ({ data }) =>
//...
}
}
};
The following error is occurring when trying to use apollo-resolvers with typescript, I noticed that several people were having the same problem and I decided to open the issue, more details below.
"dependencies": {
"@sentry/node": "4.4.2",
"apollo-errors": "1.9.0",
"apollo-resolvers": "1.4.1",
"apollo-server-micro": "2.3.1",
"bcryptjs": "2.4.3",
"bluebird": "3.5.3",
"ccxt": "1.18.36",
"date-fns": "1.30.1",
"dotenv": "6.2.0",
"graphql": "14.0.2",
"i18nh": "0.0.4",
"idx": "2.5.2",
"jsonwebtoken": "8.4.0",
"micro": "9.3.3",
"micro-compose": "0.0.3",
"micro-cors": "0.1.1",
"mongoose": "5.3.16",
"ramda": "0.26.1",
"ts-node": "7.0.1",
"validator": "10.9.0"
},
"devDependencies": {
"@types/bcryptjs": "2.4.2",
"@types/bluebird": "3.5.25",
"@types/dotenv": "6.1.0",
"@types/graphql": "14.0.3",
"@types/jsonwebtoken": "8.3.0",
"@types/micro": "7.3.3",
"@types/micro-cors": "0.1.0",
"@types/mongoose": "5.3.5",
"@types/ramda": "0.25.43",
"@types/validator": "9.4.4",
"husky": "1.2.1",
"lint-staged": "8.1.0",
"nodemon": "1.18.8",
"prettier": "1.15.3",
"tslint": "5.11.0",
"tslint-config-prettier": "1.17.0",
"tslint-config-security": "1.13.0",
"typescript": "3.2.2"
},
Example:
When calling function createResolver from baseResolver TS reports an error.
error TS2339: Property 'createResolver' does not exist on type '(root: any, args?: {}, context?: {}) => Promise'.
Example code readme.md
export const isAuthenticatedResolver = baseResolver.createResolver(
List ideas here
export default combineResolvers([
postResolvers,
userResolvers,
], { baseResolver });
Figured I'd add a couple housekeeping items.
Do you have a deployment that isn't using express? We can't use express as we have to use Serverless and I'm a little confused to the correct way to get this working inside an apollo-server-lambda environment
Post here if you want core contrib access. Looking for 1 or 2 people to help
Hi,
is it possible to use an async function in a parent resolver?
I'm trying to achieve something like this:
export const isAuthenticatedResolver = baseResolver.createResolver(
(_, args, { token }) => {
if (!token) {
throw new AuthenticationRequiredError();
}
//async part here
db.models.sessions.query(token)
.catch(e => throw new InvalidTokenError() )
);
Hi, first let me say thank you for creating this library. It's been a very useful design pattern.
Today I've run into the problem with trying to authenticate a query that looks something like this:
query {
open
sensitive
}
where open
is clear to anyone, but sensitive
requires certain permissions.
It seems when I throw an error using the apollo-resolvers
pattern, the data for open
never makes it to the client. On the bright side, the "errors"
data is correctly in the response.
Is it possible to have both the error data and open
data in the same response using this library?
Please write a note below, ideally with your slack username on apollo's slack, if you wanna know once this is released
Hello,
I'm wondering if there is a way to improve the typings definition by having the context variable being typed :
export interface ResultFunction<ResulType> {
(root: any, args: any, context: any, info: any): Promise<ResulType> | ResulType | void;
}
becomes
export interface ResultFunction<ResulType, ContextType> {
(root: any, args: any, context: ContextType, info: any): Promise<ResulType> | ResulType | void;
}
The idea behind that is that if we make some modifications on the context object (like adding new properties that would be needed by the next resolver in the pipe), we would then be able to retrieve a fully typed variable in the next resolver, instead of any.
In a perfect world, each resolver context type should augment the previous one, so that the last resolver gets a merged type of all added properties.
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.