How should I be parsing request query parameters and translate that into a database query?
Say I have a url of /projects that can sometimes be called with query parameters such as, /projects?sort=status&part=123 or possibly /projects?priority=1&owner=baz
In my express app I have route defined as:
app.get('/projects', (req,res) => {
let query = parse_request(req.query) || default_query;
database.execute(query, result => res.send(result));
});
I will be use a custom parse_request function to return the database query I want to execute based on the query parameters or a default query if no parameters were passed.
Is this a sane approach or should I be going about this a different way? Seems like the parse_request function could be a bit involved depending on how many query string options I would like to handle.
Related
I want to access resources from a SQLite database table. I have one table for accounts, one for movies and one for reviews. The reviews-table is constructed like this:
CREATE TABLE IF NOT EXISTS reviews (
id INTEGER PRIMARY KEY,
authorId INTEGER,
movieId INTEGER,
review TEXT,
rating INTEGER,
FOREIGN KEY('authorId') REFERENCES 'accounts'('id'),
FOREIGN KEY('movieId') REFERENCES 'movies'('id')
)
What I want to do is that I want to be able to get all reviews, made by one author. But I also want to be able to get all reviews, made for the same movie. Below is my code for getting all reviews made by the same user/author. The code looks the same when getting the reviews based on the movie, with a few changes.
Both of them does what I want them to do. But of course only the one written first in the file are running.
const authorId = request.params.authorId;
const query = "SELECT * FROM reviews WHERE authorId = ?";
const values = [authorId];
db.all(query, values, function (error, review) {
if (error) {
response.status(500).end();
} else if (!review) {
response.status(404).end();
} else {
response.status(200).json(review);
}
});
});
The url will look the same no matter which of them I want running; http://localhost:3000/reviews/3. How can I differentiate the url so that they know which one should run?
I have tried to experiment with query strings, but I'm not sure how that works, and after hours of searching for something that worked on my code, I gave up.
I have also been thinking about using something like
app.use(function (request, response, next) {
if (request.method == "GET" && request.uri == "/reviews/:authorId") {
response.send("Hello World");
} else {
next;
}
});
This didn't work, and it didn't work if I tried to remove ":authorId" from the url either. The page just keeps loading.
So how do I solve this?
The most dynamic would be a single route /reviews and use the query string with the params like ?author=123 or ?movie=123, they can be combined like ?author=123&movie=123. As you want to return JSON the code will be used via API, so the pretty path is usually not as important as when it is a web-url. To make the implementation effective, most people use a function where you can drop the query object in and get the where-clause, or use an ORM.
In express, when you have routers like '/reviews/:authorId' and then '/reviews/:movieId', then the second one will never be called, because the first one will always match. That is something to be careful about when organizing your express routes.
It works perfectly, with a single endpoint.
With apollo-link-rest, I have made a client that looks like this
const restLink = new RestLink({ uri: "https://example.com/" })
And export the client with a new ApolloClient({...})
Now to the question
On the same server https://example.com/, there are multiple endpoints, all with same fields but different data in each
The first query that works look like this
export const GET_PRODUCTS = gql`
query firstQuery {
products #rest(type: "data" path: "first/feed") { // the path could be second/feed and it will work with different data
id
title
}
}
`
I want all these different path into one and same json feed, because they all have the same fields, but with different data
Using aliases
You can (should be possible) use standard method to make similar queries - get many data (result) nurmally available as the same shape (node name). This is described here.
{
"1": products(....
"2": products(....
...
}
Paths can be created using variables
Results can be easy combined by iterating over data object. Problem? Only for fixed amount (not many) endpoints as query shouldn't be generated by strings manipulations.
Multiple graphql queries
You can create queries in a loop - parametrized - using Promise.all() and apollo-client client.query(. Results needs to be combined into one, too.
Custom fetch
Using custom fetch you can create a query taking an array of paths. In this case resolver should use Promise.all() on parametrized fetch requests. Combined results can be returned as single node (as required).
Bads
All these methods needs making multiple requests. Problem can be resolved by making server side REST wrapper (docs or blog).
Trying to fetch JSON data from third party url and bring it to my backend route. First, I am getting query string entered into my application's url and store them to variables and use it in third party url. Second and third query strings are not entering into the url although the query string from application url has been stored properly. Wondering if there is a proper way of doing such thing.
Also, when the JSON data is fetched, is there a way to make the format it cleaner, rather than single line of an object of 47 arrays that is overflowed into 4 lines?
app.get("/api/posts", (req, res) => {
const first = req.query.first;
const second = req.query.second;
const third = req.query.third;
axios.get(`url.com?first=${first}&second=${second}&third=${third}`)
.then(response=> res.json(response.data)
.catch(err => res.send(err))
})
I am working with node.js and express framework for my rest api server.
I have created get routes with query params and working with them.
But I want to make a functionality like facebook graph api where I can send fields with my api routes such as
/me?fields=address,birthday,email,domains.limit(10){id,name,url}
I am thinking of getting the fields from the query parameters and then splitting them based on , such as
const fields = req.query.fields;
let fieldsArray = fields.split(',');
But how can I pass the sub object attributes and retrieve them from the route like domain field from the above example.
/me?fields=address,birthday,email,domains.limit(10){id,name,url}
If you use a dot-notation like so
/me?fields=address,domains.id,domains.name&dbQueryFilters=domains.limit=10
It could mean:
I want these fields in my response:
address
domains:
id
name
Use these to query the db:
domains:
limit :10
You can add/remove such query variables until it explicitly conveys what your API does while still being very basic. It's always in your best interest to keep things simple and basic.
On the nodeJS side, you can use a library like flat to unflatten the query object:
var fields = flat.unflatten(req.query.fields)
Try sending out the request like:
/me?fields=address,birthday,email,domains.limit%2810%29%7Bid%2Cname%2Curl%7D
There is code for every special character you can get it by doing:
escape("domains.limit(10){id,name,url}") // Returns domains.limit%2810%29%7Bid%2Cname%2Curl%7D
More details: JavaScript escape() Function
Hope this solves your issue.
In application for performance optimization we using bulk entities loading, like in "Lookup Lists" example http://www.breezejs.com/documentation/lookup-lists.
Query looks like this:
entityQuery.from('SomeBreezeAction')
.using(manager)
.execute()
.then(function (res) {
var set1 = res.results[0].first;
var set2 = res.results[0].second;
}
It working very well remotely. Breeze correctly understand entity types of both entities and generate object based on metadata. In same application we using local Breeze queries for Jasmine tests of client side logic. But query like:
entityQuery.from('SomeBreezeAction')
.using(manager)
.executeLocally()
.then(function (res) {
var set1 = res.results[0].first;
var set2 = res.results[0].second;
}
fails with error: Error: Cannot find an entityType for resourceName: 'SomeBreezeAction'. Consider adding an 'EntityQuery.toType' call to your query or calling the MetadataStore.setEntityTypeForResourceName method to register an entityType for this resourceName.
This is reasonable because we are not using toType or setEntityTypeForResourceName in this query. So my question is it possible to use toType or setEntityTypeForResourceName or something else for such queries which returns more than one entity type in one request?
If its important we define metadata manually and have not direct EF or other DB connection.
The problem is that the client has no idea what "SomeBreezeAction" means. The logic for this operation is entirely contained on the server.
What you can do though is create your own function that performs the same queries locally that "SomeBreezeAction" does on the server and combines the results returned into the same 'shape' that your server side query does. This function can then be called whenever you want a local version of the same query.