I read that Facebook's internal servers accept any queries in dev
mode, and these are cached. In production, only a pre-approved/cached
query is permitted. This was mentioned as a model which other servers
should adopt.
Does someone know what tools do they use for that? Does this process is described more detailed somewhere?
I don't know how it's down in facebook but I can explain how I did it in GraphQL Guru. As graphql is language agnostic I'll explain without being language specific.
The way persisted queries work is a client sends a query with a unique id and variables to a graphql (persisted query ready) server.
{
"id": "1234",
"varibles": {
"firtName": "John",
"lstName": "Smith"
}
}
For the id don't use a hash of the query as the this results in long id names of varying sizes, which kind of defeats the purpose.
On your server, create a file with the same name as the persisted query id, which contains the actual graphql query. Or save it in a database.
To get the graphql query you will need to intercept it via middleware. The middleware retrieves the graphql query via its id and passes the query on to the graphql endpoint. Depending on how the query was defined the middleware may need to parse it. Also, it is in the middleware where you can whitelist if the persisted query id does not exist.
Then the graphql endpoint process the query as normal.
You can see a nodejs example here https://github.com/otissv/guru-express-server/blob/master/src/routes/graphql-route.js
Related
Possibly exposing my ignorance of apollo-server but hoping someone can help: so ATM I have some schemas stitched together with #graphql-tools; all very simple, cool. I can make queries without problem.
There's a desire to add custom fields to given queries, so that we add extra data from other sources into the requested existing query template.
To explain by example: say the schema looks like this:
type User {
id
projectId
}
I'm trying to develop something so that the query getUserById($id...) can provide a template like so:
query userById($id: ID!) {
userById(id: $id) {
id
project {
id
name
# whatever other fields I want from Project type
}
}
}
And then apollo/graphql would then make a separate, asynchronous request to fetch the project for that given User.
As I understand graphql-tools, I can see resolvers allow the ability to make async requests for extra data ... but my problem is by defining project { within the query template, an error is thrown because - of course - project is not defined in the actual Schema itself.
Is there a way to filter and remove fields from a given query, somewhere in the chain of events? A custom apollo-server plugin perhaps? As I said I'm exposing my ignorance here but I've got a little lost in how apollo behaves in tandem with GraphQl.
A neighborhood has many homes. Each home is owned by a person.
Say I have this graphql query:
{
neighborhoods {
homes {
owner {
name
}
}
}
}
I can preload the owners, and that'll make the data request be a single SQL query. Fine.
But what if I don't request the owner in the graphql query, the data will still be preloaded.
And if I don't preload, the data will either be fetched in every query, or not at all since I'm not loading the belongs_to association in the resolver.
I'm not sure if this is a solved issue, or just a painpoint one must swallow when working with graphql.
Using Absinthe, DataLoader and Elixir by the way.
Most GraphQL implementations, including Absinthe, expose some kind of "info" parameter that contains information specific to the field being resolved and the request being executed. You can parse this object to determine which fields were actually requested and build your SQL query appropriately.
See this issue for a more in-depth discussion.
In order to complement what Daniel Rearden said, you have to use the info.definition to resolve nested includes.
In my application I defined an array of possible values like:
defp relationships do
[
{:person, [tasks: [:items]]]}
...
]
end
then I have a logic that iterates over the info.definition and uses this function to preload the associations.
You will use a DataLoader to lazy load your resources. Usually to fetch third party requests or perform a complex database query.
I was reading this article and it used the following query:
{
getAuthor(id: 5){
name
posts {
title
author {
name # this will be the same as the name above
}
}
}
}
Which was parsed and turned into an AST like the one below:
Clearly it is bringing back redundant information (the Author's name is asked for twice), so I was wondering how GraphQL Handles that. Does it redundantly fetch that information? Is the diagram a proper depiction of the actual AST?
Any insight into the query parsing and execution process relevant to this would be appreciated, thanks.
Edit: I know this may vary depending on the actual implementation of the GraphQl server, but I was wondering what the standard / best practice was.
Yes, GraphQL may fetch the same information multiple times in this scenario. GraphQL does not memoize the resolver function, so even if it is called with the same arguments and the same parent value, it will still run again.
This is a fairly common problem when working with databases in GraphQL. The most common solution is to utilize DataLoader, which not only batches your database requests, but also provides a cache for those requests for the duration of the GraphQL request. This way, even if a particular record is requested multiple times, it will only be fetched from the database once.
The alternative (albeit more complicated) approach is to compose a single database query based on the requested fields that executes at the root level. For example, our resolver for getAuthor could constructor a single query that would return the author, their posts and each of that post's author. With this approach, we can skip writing resolvers for the posts field on the Author type or the author field on the Post type and just utilize the default resolver behavior. However, in order to do this and avoid overfetching, we have to parse the GraphQL request inside the getAuthor resolver in order to determine which fields were requested and should therefore be included in our database query.
Forgive my terribly-worded question but here's some code to explain what I'm trying to do (slug and value are provided outside this query):
const query = `{
post(slug: "${slug}") {
content
createdAt
id <--- I want this id for my reply query
slug
}
reply(replyTo: "id") { <--- The second query in question
content
createdAt
id
slug
}
user(id: "${value}") {
username
}
}`;
I just got started with GraphQL and I'm loving the fact that I can query multiple databases in one go. It'd be great if I could also perform some "queryception" but I'm not sure if this is possible.
When thinking in terms of GraphQL, it's important to remember that each field for a given type is resolved by GraphQL simultaneously.
For example, when your post query returns a Post type, GraphQL will resolve the content and createdAt fields at the same time. Once those fields are resolved, it moved on to the next "level" of the query (for example, if content returned a type instead of a scalar, it would then try to resolve those fields.
Each of your individual queries (post, reply, and user) are actually fields of the Root Query type, and the same logic applies to them as well. That means there's no way to reference the id returned by post within reply -- both queries will be fired off at the same time.
An exception to the above exists in the form of mutations, which are actually resolved sequentially instead of simultaneously. That means, even though you still wouldn't be able to use the result of post as a variable inside your reply query, you could use context to pass the id from one to the other if both were mutations. This, however, is very hackish and requires the client to request the mutations in a specific order.
A more viable solution would be to simply handle this on the client side by breaking it up into two requests, and waiting to fire the second until the first one returns.
Lastly, you may consider reworking your schema to prevent having to have multiple queries in the first place. For example, your Post type could simply have a replies field that would resolve to all replies that correspond with the returned post's id.
I am creating an ldapjs server to provide an LDAP interface to users stored in a ReactiveDB. When a 3rd-party app performs an LDAP search for users, I want to iterate over all rows in a RethinkDB users table and send them in LDAP form.
According to the RethinkDB docs, r.table('users').getAll() will return nothing.
It seems like I might be able to do r.table('users').filter(() => true) but that would be a hack.
Is RethinkDB trying to discourage getting all rows? Unfortunately that's just what I need so that OwnCloud can sync in all of the available users via its LDAP Auth Backend.
I figured it out, simply using r.table('users') returns all documents in the table according to https://www.rethinkdb.com/api/javascript/table/. Filtering options chained after table() are not required.